aee3eba5edc3ae03270b1aed9791458f570a9341
[xonotic/darkplaces.git] / prvm_edict.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // AK new vm
21
22 #include "quakedef.h"
23 #include "progsvm.h"
24
25 prvm_prog_t *prog;
26
27 static prvm_prog_t prog_list[PRVM_MAXPROGS];
28
29 int             prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
30
31 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
32 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s);
33
34 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
35 cvar_t prvm_boundscheck = {0, "prvm_boundscheck", "1", "enables detection of out of bounds memory access in the QuakeC code being run (in other words, prevents really exceedingly bad QuakeC code from doing nasty things to your computer)"};
36 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
37 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
38 // LordHavoc: counts usage of each QuakeC statement
39 cvar_t prvm_statementprofiling = {0, "prvm_statementprofiling", "0", "counts how many times each QuakeC statement has been executed, these counts are displayed in prvm_printfunction output (if enabled)"};
40
41 extern sizebuf_t vm_tempstringsbuf;
42
43 //============================================================================
44 // mempool handling
45
46 /*
47 ===============
48 PRVM_MEM_Alloc
49 ===============
50 */
51 void PRVM_MEM_Alloc(void)
52 {
53         int i;
54
55         // reserve space for the null entity aka world
56         // check bound of max_edicts
57         prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
58         prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
59
60         // edictprivate_size has to be min as big prvm_edict_private_t
61         prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
62
63         // alloc edicts
64         prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
65
66         // alloc edict private space
67         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
68
69         // alloc edict fields
70         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
71
72         // set edict pointers
73         for(i = 0; i < prog->max_edicts; i++)
74         {
75                 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
76                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
77         }
78 }
79
80 /*
81 ===============
82 PRVM_MEM_IncreaseEdicts
83 ===============
84 */
85 void PRVM_MEM_IncreaseEdicts(void)
86 {
87         int             i;
88         int             oldmaxedicts = prog->max_edicts;
89         void    *oldedictsfields = prog->edictsfields;
90         void    *oldedictprivate = prog->edictprivate;
91
92         if(prog->max_edicts >= prog->limit_edicts)
93                 return;
94
95         PRVM_GCALL(begin_increase_edicts)();
96
97         // increase edicts
98         prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
99
100         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
101         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
102
103         memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
104         memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
105
106         //set e and v pointers
107         for(i = 0; i < prog->max_edicts; i++)
108         {
109                 prog->edicts[i].priv.required  = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
110                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
111         }
112
113         PRVM_GCALL(end_increase_edicts)();
114
115         Mem_Free(oldedictsfields);
116         Mem_Free(oldedictprivate);
117 }
118
119 //============================================================================
120 // normal prvm
121
122 int PRVM_ED_FindFieldOffset(const char *field)
123 {
124         ddef_t *d;
125         d = PRVM_ED_FindField(field);
126         if (!d)
127                 return -1;
128         return d->ofs;
129 }
130
131 int PRVM_ED_FindGlobalOffset(const char *global)
132 {
133         ddef_t *d;
134         d = PRVM_ED_FindGlobal(global);
135         if (!d)
136                 return -1;
137         return d->ofs;
138 }
139
140 func_t PRVM_ED_FindFunctionOffset(const char *function)
141 {
142         mfunction_t *f;
143         f = PRVM_ED_FindFunction(function);
144         if (!f)
145                 return 0;
146         return (func_t)(f - prog->functions);
147 }
148
149 qboolean PRVM_ProgLoaded(int prognr)
150 {
151         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
152                 return FALSE;
153
154         return (prog_list[prognr].loaded ? TRUE : FALSE);
155 }
156
157 /*
158 =================
159 PRVM_SetProgFromString
160 =================
161 */
162 // perhaps add a return value when the str doesnt exist
163 qboolean PRVM_SetProgFromString(const char *str)
164 {
165         int i = 0;
166         for(; i < PRVM_MAXPROGS ; i++)
167                 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
168                 {
169                         if(prog_list[i].loaded)
170                         {
171                                 prog = &prog_list[i];
172                                 return TRUE;
173                         }
174                         else
175                         {
176                                 Con_Printf("%s not loaded !\n",PRVM_NAME);
177                                 return FALSE;
178                         }
179                 }
180
181         Con_Printf("Invalid program name %s !\n", str);
182         return FALSE;
183 }
184
185 /*
186 =================
187 PRVM_SetProg
188 =================
189 */
190 void PRVM_SetProg(int prognr)
191 {
192         if(0 <= prognr && prognr < PRVM_MAXPROGS)
193         {
194                 if(prog_list[prognr].loaded)
195                         prog = &prog_list[prognr];
196                 else
197                         PRVM_ERROR("%i not loaded !", prognr);
198                 return;
199         }
200         PRVM_ERROR("Invalid program number %i", prognr);
201 }
202
203 /*
204 =================
205 PRVM_ED_ClearEdict
206
207 Sets everything to NULL
208 =================
209 */
210 void PRVM_ED_ClearEdict (prvm_edict_t *e)
211 {
212         memset (e->fields.vp, 0, prog->progs->entityfields * 4);
213         e->priv.required->free = false;
214
215         // AK: Let the init_edict function determine if something needs to be initialized
216         PRVM_GCALL(init_edict)(e);
217 }
218
219 /*
220 =================
221 PRVM_ED_Alloc
222
223 Either finds a free edict, or allocates a new one.
224 Try to avoid reusing an entity that was recently freed, because it
225 can cause the client to think the entity morphed into something else
226 instead of being removed and recreated, which can cause interpolated
227 angles and bad trails.
228 =================
229 */
230 prvm_edict_t *PRVM_ED_Alloc (void)
231 {
232         int                     i;
233         prvm_edict_t            *e;
234
235         // the client qc dont need maxclients
236         // thus it doesnt need to use svs.maxclients
237         // AK:  changed i=svs.maxclients+1
238         // AK:  changed so the edict 0 wont spawn -> used as reserved/world entity
239         //              although the menu/client has no world
240         for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
241         {
242                 e = PRVM_EDICT_NUM(i);
243                 // the first couple seconds of server time can involve a lot of
244                 // freeing and allocating, so relax the replacement policy
245                 if (e->priv.required->free && ( e->priv.required->freetime < 2 || prog->globaloffsets.time < 0 || (PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float - e->priv.required->freetime) > 0.5 ) )
246                 {
247                         PRVM_ED_ClearEdict (e);
248                         return e;
249                 }
250         }
251
252         if (i == prog->limit_edicts)
253                 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
254
255         prog->num_edicts++;
256         if (prog->num_edicts >= prog->max_edicts)
257                 PRVM_MEM_IncreaseEdicts();
258
259         e = PRVM_EDICT_NUM(i);
260         PRVM_ED_ClearEdict (e);
261
262         return e;
263 }
264
265 /*
266 =================
267 PRVM_ED_Free
268
269 Marks the edict as free
270 FIXME: walk all entities and NULL out references to this entity
271 =================
272 */
273 void PRVM_ED_Free (prvm_edict_t *ed)
274 {
275         // dont delete the null entity (world) or reserved edicts
276         if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
277                 return;
278
279         PRVM_GCALL(free_edict)(ed);
280
281         ed->priv.required->free = true;
282         ed->priv.required->freetime = prog->globaloffsets.time >= 0 ? PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float : 0;
283 }
284
285 //===========================================================================
286
287 /*
288 ============
289 PRVM_ED_GlobalAtOfs
290 ============
291 */
292 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
293 {
294         ddef_t          *def;
295         int                     i;
296
297         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
298         {
299                 def = &prog->globaldefs[i];
300                 if (def->ofs == ofs)
301                         return def;
302         }
303         return NULL;
304 }
305
306 /*
307 ============
308 PRVM_ED_FieldAtOfs
309 ============
310 */
311 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
312 {
313         ddef_t          *def;
314         int                     i;
315
316         for (i=0 ; i<prog->progs->numfielddefs ; i++)
317         {
318                 def = &prog->fielddefs[i];
319                 if (def->ofs == ofs)
320                         return def;
321         }
322         return NULL;
323 }
324
325 /*
326 ============
327 PRVM_ED_FindField
328 ============
329 */
330 ddef_t *PRVM_ED_FindField (const char *name)
331 {
332         ddef_t *def;
333         int i;
334
335         for (i=0 ; i<prog->progs->numfielddefs ; i++)
336         {
337                 def = &prog->fielddefs[i];
338                 if (!strcmp(PRVM_GetString(def->s_name), name))
339                         return def;
340         }
341         return NULL;
342 }
343
344 /*
345 ============
346 PRVM_ED_FindGlobal
347 ============
348 */
349 ddef_t *PRVM_ED_FindGlobal (const char *name)
350 {
351         ddef_t *def;
352         int i;
353
354         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
355         {
356                 def = &prog->globaldefs[i];
357                 if (!strcmp(PRVM_GetString(def->s_name), name))
358                         return def;
359         }
360         return NULL;
361 }
362
363
364 /*
365 ============
366 PRVM_ED_FindFunction
367 ============
368 */
369 mfunction_t *PRVM_ED_FindFunction (const char *name)
370 {
371         mfunction_t             *func;
372         int                             i;
373
374         for (i=0 ; i<prog->progs->numfunctions ; i++)
375         {
376                 func = &prog->functions[i];
377                 if (!strcmp(PRVM_GetString(func->s_name), name))
378                         return func;
379         }
380         return NULL;
381 }
382
383
384 /*
385 ============
386 PRVM_ValueString
387
388 Returns a string describing *data in a type specific manner
389 =============
390 */
391 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
392 {
393         static char line[MAX_INPUTLINE];
394         ddef_t *def;
395         mfunction_t *f;
396         int n;
397
398         type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
399
400         switch (type)
401         {
402         case ev_string:
403                 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
404                 break;
405         case ev_entity:
406                 n = val->edict;
407                 if (n < 0 || n >= prog->limit_edicts)
408                         sprintf (line, "entity %i (invalid!)", n);
409                 else
410                         sprintf (line, "entity %i", n);
411                 break;
412         case ev_function:
413                 f = prog->functions + val->function;
414                 sprintf (line, "%s()", PRVM_GetString(f->s_name));
415                 break;
416         case ev_field:
417                 def = PRVM_ED_FieldAtOfs ( val->_int );
418                 sprintf (line, ".%s", PRVM_GetString(def->s_name));
419                 break;
420         case ev_void:
421                 sprintf (line, "void");
422                 break;
423         case ev_float:
424                 // LordHavoc: changed from %5.1f to %10.4f
425                 sprintf (line, "%10.4f", val->_float);
426                 break;
427         case ev_vector:
428                 // LordHavoc: changed from %5.1f to %10.4f
429                 sprintf (line, "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
430                 break;
431         case ev_pointer:
432                 sprintf (line, "pointer");
433                 break;
434         default:
435                 sprintf (line, "bad type %i", (int) type);
436                 break;
437         }
438
439         return line;
440 }
441
442 /*
443 ============
444 PRVM_UglyValueString
445
446 Returns a string describing *data in a type specific manner
447 Easier to parse than PR_ValueString
448 =============
449 */
450 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
451 {
452         static char line[MAX_INPUTLINE];
453         int i;
454         const char *s;
455         ddef_t *def;
456         mfunction_t *f;
457
458         type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
459
460         switch (type)
461         {
462         case ev_string:
463                 // Parse the string a bit to turn special characters
464                 // (like newline, specifically) into escape codes,
465                 // this fixes saving games from various mods
466                 s = PRVM_GetString (val->string);
467                 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
468                 {
469                         if (*s == '\n')
470                         {
471                                 line[i++] = '\\';
472                                 line[i++] = 'n';
473                         }
474                         else if (*s == '\r')
475                         {
476                                 line[i++] = '\\';
477                                 line[i++] = 'r';
478                         }
479                         else
480                                 line[i++] = *s;
481                         s++;
482                 }
483                 line[i] = '\0';
484                 break;
485         case ev_entity:
486                 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
487                 break;
488         case ev_function:
489                 f = prog->functions + val->function;
490                 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
491                 break;
492         case ev_field:
493                 def = PRVM_ED_FieldAtOfs ( val->_int );
494                 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
495                 break;
496         case ev_void:
497                 dpsnprintf (line, sizeof (line), "void");
498                 break;
499         case ev_float:
500                 dpsnprintf (line, sizeof (line), "%f", val->_float);
501                 break;
502         case ev_vector:
503                 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
504                 break;
505         default:
506                 dpsnprintf (line, sizeof (line), "bad type %i", type);
507                 break;
508         }
509
510         return line;
511 }
512
513 /*
514 ============
515 PRVM_GlobalString
516
517 Returns a string with a description and the contents of a global,
518 padded to 20 field width
519 ============
520 */
521 char *PRVM_GlobalString (int ofs)
522 {
523         char    *s;
524         //size_t        i;
525         ddef_t  *def;
526         void    *val;
527         static char     line[128];
528
529         val = (void *)&prog->globals.generic[ofs];
530         def = PRVM_ED_GlobalAtOfs(ofs);
531         if (!def)
532                 sprintf (line,"GLOBAL%i", ofs);
533         else
534         {
535                 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
536                 sprintf (line,"%s (=%s)", PRVM_GetString(def->s_name), s);
537         }
538
539         //i = strlen(line);
540         //for ( ; i<20 ; i++)
541         //      strcat (line," ");
542         //strcat (line," ");
543
544         return line;
545 }
546
547 char *PRVM_GlobalStringNoContents (int ofs)
548 {
549         //size_t        i;
550         ddef_t  *def;
551         static char     line[128];
552
553         def = PRVM_ED_GlobalAtOfs(ofs);
554         if (!def)
555                 sprintf (line,"GLOBAL%i", ofs);
556         else
557                 sprintf (line,"%s", PRVM_GetString(def->s_name));
558
559         //i = strlen(line);
560         //for ( ; i<20 ; i++)
561         //      strcat (line," ");
562         //strcat (line," ");
563
564         return line;
565 }
566
567
568 /*
569 =============
570 PRVM_ED_Print
571
572 For debugging
573 =============
574 */
575 // LordHavoc: optimized this to print out much more quickly (tempstring)
576 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
577 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
578 {
579         size_t  l;
580         ddef_t  *d;
581         int             *v;
582         int             i, j;
583         const char      *name;
584         int             type;
585         char    tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
586
587         if (ed->priv.required->free)
588         {
589                 Con_Printf("%s: FREE\n",PRVM_NAME);
590                 return;
591         }
592
593         tempstring[0] = 0;
594         sprintf(tempstring, "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
595         for (i=1 ; i<prog->progs->numfielddefs ; i++)
596         {
597                 d = &prog->fielddefs[i];
598                 name = PRVM_GetString(d->s_name);
599                 if (name[strlen(name)-2] == '_')
600                         continue;       // skip _x, _y, _z vars
601
602                 // Check Field Name Wildcard
603                 if(wildcard_fieldname)
604                         if( !matchpattern(name, wildcard_fieldname, 1) )
605                                 // Didn't match; skip
606                                 continue;
607
608                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
609
610         // if the value is still all 0, skip the field
611                 type = d->type & ~DEF_SAVEGLOBAL;
612
613                 for (j=0 ; j<prvm_type_size[type] ; j++)
614                         if (v[j])
615                                 break;
616                 if (j == prvm_type_size[type])
617                         continue;
618
619                 if (strlen(name) > sizeof(tempstring2)-4)
620                 {
621                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
622                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
623                         tempstring2[sizeof(tempstring2)-1] = 0;
624                         name = tempstring2;
625                 }
626                 strlcat(tempstring, name, sizeof(tempstring));
627                 for (l = strlen(name);l < 14;l++)
628                         strlcat(tempstring, " ", sizeof(tempstring));
629                 strlcat(tempstring, " ", sizeof(tempstring));
630
631                 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
632                 if (strlen(name) > sizeof(tempstring2)-4)
633                 {
634                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
635                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
636                         tempstring2[sizeof(tempstring2)-1] = 0;
637                         name = tempstring2;
638                 }
639                 strlcat(tempstring, name, sizeof(tempstring));
640                 strlcat(tempstring, "\n", sizeof(tempstring));
641                 if (strlen(tempstring) >= sizeof(tempstring)/2)
642                 {
643                         Con_Print(tempstring);
644                         tempstring[0] = 0;
645                 }
646         }
647         if (tempstring[0])
648                 Con_Print(tempstring);
649 }
650
651 /*
652 =============
653 PRVM_ED_Write
654
655 For savegames
656 =============
657 */
658 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
659 {
660         ddef_t  *d;
661         int             *v;
662         int             i, j;
663         const char      *name;
664         int             type;
665
666         FS_Print(f, "{\n");
667
668         if (ed->priv.required->free)
669         {
670                 FS_Print(f, "}\n");
671                 return;
672         }
673
674         for (i=1 ; i<prog->progs->numfielddefs ; i++)
675         {
676                 d = &prog->fielddefs[i];
677                 name = PRVM_GetString(d->s_name);
678                 if (name[strlen(name)-2] == '_')
679                         continue;       // skip _x, _y, _z vars
680
681                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
682
683         // if the value is still all 0, skip the field
684                 type = d->type & ~DEF_SAVEGLOBAL;
685                 for (j=0 ; j<prvm_type_size[type] ; j++)
686                         if (v[j])
687                                 break;
688                 if (j == prvm_type_size[type])
689                         continue;
690
691                 FS_Printf(f,"\"%s\" ",name);
692                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
693         }
694
695         FS_Print(f, "}\n");
696 }
697
698 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
699 {
700         PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
701 }
702
703 /*
704 =============
705 PRVM_ED_PrintEdicts_f
706
707 For debugging, prints all the entities in the current server
708 =============
709 */
710 void PRVM_ED_PrintEdicts_f (void)
711 {
712         int             i;
713         const char *wildcard_fieldname;
714
715         if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
716         {
717                 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
718                 return;
719         }
720
721         PRVM_Begin;
722         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
723                 return;
724
725         if( Cmd_Argc() == 3)
726                 wildcard_fieldname = Cmd_Argv(2);
727         else
728                 wildcard_fieldname = NULL;
729
730         Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
731         for (i=0 ; i<prog->num_edicts ; i++)
732                 PRVM_ED_PrintNum (i, wildcard_fieldname);
733
734         PRVM_End;
735 }
736
737 /*
738 =============
739 PRVM_ED_PrintEdict_f
740
741 For debugging, prints a single edict
742 =============
743 */
744 void PRVM_ED_PrintEdict_f (void)
745 {
746         int             i;
747         const char      *wildcard_fieldname;
748
749         if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
750         {
751                 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
752                 return;
753         }
754
755         PRVM_Begin;
756         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
757                 return;
758
759         i = atoi (Cmd_Argv(2));
760         if (i >= prog->num_edicts)
761         {
762                 Con_Print("Bad edict number\n");
763                 PRVM_End;
764                 return;
765         }
766         if( Cmd_Argc() == 4)
767                 // Optional Wildcard Provided
768                 wildcard_fieldname = Cmd_Argv(3);
769         else
770                 // Use All
771                 wildcard_fieldname = NULL;
772         PRVM_ED_PrintNum (i, wildcard_fieldname);
773
774         PRVM_End;
775 }
776
777 /*
778 =============
779 PRVM_ED_Count
780
781 For debugging
782 =============
783 */
784 // 2 possibilities : 1. just displaying the active edict count
785 //                                       2. making a function pointer [x]
786 void PRVM_ED_Count_f (void)
787 {
788         int             i;
789         prvm_edict_t    *ent;
790         int             active;
791
792         if(Cmd_Argc() != 2)
793         {
794                 Con_Print("prvm_count <program name>\n");
795                 return;
796         }
797
798         PRVM_Begin;
799         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
800                 return;
801
802         if(prog->count_edicts)
803                 prog->count_edicts();
804         else
805         {
806                 active = 0;
807                 for (i=0 ; i<prog->num_edicts ; i++)
808                 {
809                         ent = PRVM_EDICT_NUM(i);
810                         if (ent->priv.required->free)
811                                 continue;
812                         active++;
813                 }
814
815                 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
816                 Con_Printf("active    :%3i\n", active);
817         }
818
819         PRVM_End;
820 }
821
822 /*
823 ==============================================================================
824
825                                         ARCHIVING GLOBALS
826
827 FIXME: need to tag constants, doesn't really work
828 ==============================================================================
829 */
830
831 /*
832 =============
833 PRVM_ED_WriteGlobals
834 =============
835 */
836 void PRVM_ED_WriteGlobals (qfile_t *f)
837 {
838         ddef_t          *def;
839         int                     i;
840         const char              *name;
841         int                     type;
842
843         FS_Print(f,"{\n");
844         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
845         {
846                 def = &prog->globaldefs[i];
847                 type = def->type;
848                 if ( !(def->type & DEF_SAVEGLOBAL) )
849                         continue;
850                 type &= ~DEF_SAVEGLOBAL;
851
852                 if (type != ev_string && type != ev_float && type != ev_entity)
853                         continue;
854
855                 name = PRVM_GetString(def->s_name);
856                 FS_Printf(f,"\"%s\" ", name);
857                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
858         }
859         FS_Print(f,"}\n");
860 }
861
862 /*
863 =============
864 PRVM_ED_ParseGlobals
865 =============
866 */
867 void PRVM_ED_ParseGlobals (const char *data)
868 {
869         char keyname[MAX_INPUTLINE];
870         ddef_t *key;
871
872         while (1)
873         {
874                 // parse key
875                 if (!COM_ParseToken_Simple(&data, false, false))
876                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
877                 if (com_token[0] == '}')
878                         break;
879
880                 strlcpy (keyname, com_token, sizeof(keyname));
881
882                 // parse value
883                 if (!COM_ParseToken_Simple(&data, false, true))
884                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
885
886                 if (com_token[0] == '}')
887                         PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
888
889                 key = PRVM_ED_FindGlobal (keyname);
890                 if (!key)
891                 {
892                         Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
893                         continue;
894                 }
895
896                 if (!PRVM_ED_ParseEpair(NULL, key, com_token))
897                         PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
898         }
899 }
900
901 //============================================================================
902
903
904 /*
905 =============
906 PRVM_ED_ParseEval
907
908 Can parse either fields or globals
909 returns false if error
910 =============
911 */
912 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s)
913 {
914         int i, l;
915         char *new_p;
916         ddef_t *def;
917         prvm_eval_t *val;
918         mfunction_t *func;
919
920         if (ent)
921                 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
922         else
923                 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
924         switch (key->type & ~DEF_SAVEGLOBAL)
925         {
926         case ev_string:
927                 l = (int)strlen(s) + 1;
928                 val->string = PRVM_AllocString(l, &new_p);
929                 for (i = 0;i < l;i++)
930                 {
931                         if (s[i] == '\\' && i < l-1)
932                         {
933                                 i++;
934                                 if (s[i] == 'n')
935                                         *new_p++ = '\n';
936                                 else if (s[i] == 'r')
937                                         *new_p++ = '\r';
938                                 else
939                                         *new_p++ = s[i];
940                         }
941                         else
942                                 *new_p++ = s[i];
943                 }
944                 break;
945
946         case ev_float:
947                 while (*s && *s <= ' ')
948                         s++;
949                 val->_float = atof(s);
950                 break;
951
952         case ev_vector:
953                 for (i = 0;i < 3;i++)
954                 {
955                         while (*s && *s <= ' ')
956                                 s++;
957                         if (!*s)
958                                 break;
959                         val->vector[i] = atof(s);
960                         while (*s > ' ')
961                                 s++;
962                         if (!*s)
963                                 break;
964                 }
965                 break;
966
967         case ev_entity:
968                 while (*s && *s <= ' ')
969                         s++;
970                 i = atoi(s);
971                 if (i >= prog->limit_edicts)
972                         Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, (unsigned int)MAX_EDICTS, PRVM_NAME);
973                 while (i >= prog->max_edicts)
974                         PRVM_MEM_IncreaseEdicts();
975                 // if IncreaseEdicts was called the base pointer needs to be updated
976                 if (ent)
977                         val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
978                 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
979                 break;
980
981         case ev_field:
982                 def = PRVM_ED_FindField(s);
983                 if (!def)
984                 {
985                         Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
986                         return false;
987                 }
988                 val->_int = def->ofs;
989                 break;
990
991         case ev_function:
992                 func = PRVM_ED_FindFunction(s);
993                 if (!func)
994                 {
995                         Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
996                         return false;
997                 }
998                 val->function = func - prog->functions;
999                 break;
1000
1001         default:
1002                 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1003                 return false;
1004         }
1005         return true;
1006 }
1007
1008 /*
1009 =============
1010 PRVM_GameCommand_f
1011
1012 Console command to send a string to QC function GameCommand of the
1013 indicated progs
1014
1015 Usage:
1016   sv_cmd adminmsg 3 "do not teamkill"
1017   cl_cmd someclientcommand
1018   menu_cmd somemenucommand
1019
1020 All progs can support this extension; sg calls it in server QC, cg in client
1021 QC, mg in menu QC.
1022 =============
1023 */
1024 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1025 {
1026         if(Cmd_Argc() < 1)
1027         {
1028                 Con_Printf("%s text...\n", whichcmd);
1029                 return;
1030         }
1031
1032         PRVM_Begin;
1033         if(!PRVM_SetProgFromString(whichprogs))
1034         // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1035         // also, it makes printing error messages easier!
1036         {
1037                 Con_Printf("%s program not loaded.\n", whichprogs);
1038                 return;
1039         }
1040
1041         if(!prog->funcoffsets.GameCommand)
1042         {
1043                 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1044         }
1045         else
1046         {
1047                 int restorevm_tempstringsbuf_cursize;
1048                 const char *s;
1049
1050                 s = Cmd_Args();
1051
1052                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1053                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1054                 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1055                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1056         }
1057
1058         PRVM_End;
1059 }
1060 void PRVM_GameCommand_Server_f(void)
1061 {
1062         PRVM_GameCommand("server", "sv_cmd");
1063 }
1064 void PRVM_GameCommand_Client_f(void)
1065 {
1066         PRVM_GameCommand("client", "cl_cmd");
1067 }
1068 void PRVM_GameCommand_Menu_f(void)
1069 {
1070         PRVM_GameCommand("menu", "menu_cmd");
1071 }
1072
1073 /*
1074 =============
1075 PRVM_ED_EdictSet_f
1076
1077 Console command to set a field of a specified edict
1078 =============
1079 */
1080 void PRVM_ED_EdictSet_f(void)
1081 {
1082         prvm_edict_t *ed;
1083         ddef_t *key;
1084
1085         if(Cmd_Argc() != 5)
1086         {
1087                 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1088                 return;
1089         }
1090
1091         PRVM_Begin;
1092         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1093         {
1094                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1095                 return;
1096         }
1097
1098         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1099
1100         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1101                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1102         else
1103                 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4));
1104
1105         PRVM_End;
1106 }
1107
1108 /*
1109 ====================
1110 PRVM_ED_ParseEdict
1111
1112 Parses an edict out of the given string, returning the new position
1113 ed should be a properly initialized empty edict.
1114 Used for initial level load and for savegames.
1115 ====================
1116 */
1117 extern cvar_t developer_entityparsing;
1118 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1119 {
1120         ddef_t *key;
1121         qboolean anglehack;
1122         qboolean init;
1123         char keyname[256];
1124         size_t n;
1125
1126         init = false;
1127
1128 // go through all the dictionary pairs
1129         while (1)
1130         {
1131         // parse key
1132                 if (!COM_ParseToken_Simple(&data, false, false))
1133                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1134                 if (developer_entityparsing.integer)
1135                         Con_Printf("Key: \"%s\"", com_token);
1136                 if (com_token[0] == '}')
1137                         break;
1138
1139                 // anglehack is to allow QuakeEd to write single scalar angles
1140                 // and allow them to be turned into vectors. (FIXME...)
1141                 if (!strcmp(com_token, "angle"))
1142                 {
1143                         strlcpy (com_token, "angles", sizeof(com_token));
1144                         anglehack = true;
1145                 }
1146                 else
1147                         anglehack = false;
1148
1149                 // FIXME: change light to _light to get rid of this hack
1150                 if (!strcmp(com_token, "light"))
1151                         strlcpy (com_token, "light_lev", sizeof(com_token));    // hack for single light def
1152
1153                 strlcpy (keyname, com_token, sizeof(keyname));
1154
1155                 // another hack to fix keynames with trailing spaces
1156                 n = strlen(keyname);
1157                 while (n && keyname[n-1] == ' ')
1158                 {
1159                         keyname[n-1] = 0;
1160                         n--;
1161                 }
1162
1163         // parse value
1164                 if (!COM_ParseToken_Simple(&data, false, true))
1165                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1166                 if (developer_entityparsing.integer)
1167                         Con_Printf(" \"%s\"\n", com_token);
1168
1169                 if (com_token[0] == '}')
1170                         PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1171
1172                 init = true;
1173
1174                 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1175                 if (!keyname[0])
1176                         continue;
1177
1178 // keynames with a leading underscore are used for utility comments,
1179 // and are immediately discarded by quake
1180                 if (keyname[0] == '_')
1181                         continue;
1182
1183                 key = PRVM_ED_FindField (keyname);
1184                 if (!key)
1185                 {
1186                         Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1187                         continue;
1188                 }
1189
1190                 if (anglehack)
1191                 {
1192                         char    temp[32];
1193                         strlcpy (temp, com_token, sizeof(temp));
1194                         sprintf (com_token, "0 %s 0", temp);
1195                 }
1196
1197                 if (!PRVM_ED_ParseEpair(ent, key, com_token))
1198                         PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1199         }
1200
1201         if (!init)
1202                 ent->priv.required->free = true;
1203
1204         return data;
1205 }
1206
1207
1208 /*
1209 ================
1210 PRVM_ED_LoadFromFile
1211
1212 The entities are directly placed in the array, rather than allocated with
1213 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1214 number references out of order.
1215
1216 Creates a server's entity / program execution context by
1217 parsing textual entity definitions out of an ent file.
1218
1219 Used for both fresh maps and savegame loads.  A fresh map would also need
1220 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1221 ================
1222 */
1223 void PRVM_ED_LoadFromFile (const char *data)
1224 {
1225         prvm_edict_t *ent;
1226         int parsed, inhibited, spawned, died;
1227         mfunction_t *func;
1228
1229         parsed = 0;
1230         inhibited = 0;
1231         spawned = 0;
1232         died = 0;
1233
1234
1235 // parse ents
1236         while (1)
1237         {
1238 // parse the opening brace
1239                 if (!COM_ParseToken_Simple(&data, false, false))
1240                         break;
1241                 if (com_token[0] != '{')
1242                         PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1243
1244                 // CHANGED: this is not conform to PR_LoadFromFile
1245                 if(prog->loadintoworld)
1246                 {
1247                         prog->loadintoworld = false;
1248                         ent = PRVM_EDICT_NUM(0);
1249                 }
1250                 else
1251                         ent = PRVM_ED_Alloc();
1252
1253                 // clear it
1254                 if (ent != prog->edicts)        // hack
1255                         memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1256
1257                 data = PRVM_ED_ParseEdict (data, ent);
1258                 parsed++;
1259
1260                 // remove the entity ?
1261                 if(prog->load_edict && !prog->load_edict(ent))
1262                 {
1263                         PRVM_ED_Free(ent);
1264                         inhibited++;
1265                         continue;
1266                 }
1267
1268 //
1269 // immediately call spawn function, but only if there is a self global and a classname
1270 //
1271                 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1272                 {
1273                         string_t handle =  PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1274                         if (!handle)
1275                         {
1276                                 Con_Print("No classname for:\n");
1277                                 PRVM_ED_Print(ent, NULL);
1278                                 PRVM_ED_Free (ent);
1279                                 continue;
1280                         }
1281
1282                         // look for the spawn function
1283                         func = PRVM_ED_FindFunction (PRVM_GetString(handle));
1284
1285                         if (!func)
1286                         {
1287                                 // check for OnEntityNoSpawnFunction
1288                                 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1289                                 {
1290                                         // self = ent
1291                                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1292                                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1293                                 }
1294                                 else
1295                                 {
1296                                         if (developer.integer) // don't confuse non-developers with errors
1297                                         {
1298                                                 Con_Print("No spawn function for:\n");
1299                                                 PRVM_ED_Print(ent, NULL);
1300                                         }
1301                                         PRVM_ED_Free (ent);
1302                                         continue;
1303                                 }
1304                         }
1305                         else
1306                         {
1307                                 // self = ent
1308                                 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1309                                 PRVM_ExecuteProgram (func - prog->functions, "");
1310                         }
1311                 }
1312
1313                 spawned++;
1314                 if (ent->priv.required->free)
1315                         died++;
1316         }
1317
1318         Con_DPrintf("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", PRVM_NAME, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died);
1319 }
1320
1321 void PRVM_FindOffsets(void)
1322 {
1323         // field and global searches use -1 for NULL
1324         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1325         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1326         // functions use 0 for NULL
1327         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1328
1329         // server and client qc use a lot of similar fields, so this is combined
1330         prog->fieldoffsets.SendEntity                     = PRVM_ED_FindFieldOffset("SendEntity");
1331         prog->fieldoffsets.Version                        = PRVM_ED_FindFieldOffset("Version");
1332         prog->fieldoffsets.alpha                          = PRVM_ED_FindFieldOffset("alpha");
1333         prog->fieldoffsets.ammo_cells1                    = PRVM_ED_FindFieldOffset("ammo_cells1");
1334         prog->fieldoffsets.ammo_lava_nails                = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1335         prog->fieldoffsets.ammo_multi_rockets             = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1336         prog->fieldoffsets.ammo_nails1                    = PRVM_ED_FindFieldOffset("ammo_nails1");
1337         prog->fieldoffsets.ammo_plasma                    = PRVM_ED_FindFieldOffset("ammo_plasma");
1338         prog->fieldoffsets.ammo_rockets1                  = PRVM_ED_FindFieldOffset("ammo_rockets1");
1339         prog->fieldoffsets.ammo_shells1                   = PRVM_ED_FindFieldOffset("ammo_shells1");
1340         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1341         prog->fieldoffsets.button3                        = PRVM_ED_FindFieldOffset("button3");
1342         prog->fieldoffsets.button4                        = PRVM_ED_FindFieldOffset("button4");
1343         prog->fieldoffsets.button5                        = PRVM_ED_FindFieldOffset("button5");
1344         prog->fieldoffsets.button6                        = PRVM_ED_FindFieldOffset("button6");
1345         prog->fieldoffsets.button7                        = PRVM_ED_FindFieldOffset("button7");
1346         prog->fieldoffsets.button8                        = PRVM_ED_FindFieldOffset("button8");
1347         prog->fieldoffsets.button9                        = PRVM_ED_FindFieldOffset("button9");
1348         prog->fieldoffsets.button10                       = PRVM_ED_FindFieldOffset("button10");
1349         prog->fieldoffsets.button11                       = PRVM_ED_FindFieldOffset("button11");
1350         prog->fieldoffsets.button12                       = PRVM_ED_FindFieldOffset("button12");
1351         prog->fieldoffsets.button13                       = PRVM_ED_FindFieldOffset("button13");
1352         prog->fieldoffsets.button14                       = PRVM_ED_FindFieldOffset("button14");
1353         prog->fieldoffsets.button15                       = PRVM_ED_FindFieldOffset("button15");
1354         prog->fieldoffsets.button16                       = PRVM_ED_FindFieldOffset("button16");
1355         prog->fieldoffsets.buttonchat                     = PRVM_ED_FindFieldOffset("buttonchat");
1356         prog->fieldoffsets.buttonuse                      = PRVM_ED_FindFieldOffset("buttonuse");
1357         prog->fieldoffsets.chain                          = PRVM_ED_FindFieldOffset("chain");
1358         prog->fieldoffsets.classname                      = PRVM_ED_FindFieldOffset("classname");
1359         prog->fieldoffsets.clientcolors                   = PRVM_ED_FindFieldOffset("clientcolors");
1360         prog->fieldoffsets.color                          = PRVM_ED_FindFieldOffset("color");
1361         prog->fieldoffsets.colormod                       = PRVM_ED_FindFieldOffset("colormod");
1362         prog->fieldoffsets.contentstransition             = PRVM_ED_FindFieldOffset("contentstransition");
1363         prog->fieldoffsets.cursor_active                  = PRVM_ED_FindFieldOffset("cursor_active");
1364         prog->fieldoffsets.cursor_screen                  = PRVM_ED_FindFieldOffset("cursor_screen");
1365         prog->fieldoffsets.cursor_trace_endpos            = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1366         prog->fieldoffsets.cursor_trace_ent               = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1367         prog->fieldoffsets.cursor_trace_start             = PRVM_ED_FindFieldOffset("cursor_trace_start");
1368         prog->fieldoffsets.customizeentityforclient       = PRVM_ED_FindFieldOffset("customizeentityforclient");
1369         prog->fieldoffsets.dimension_hit                  = PRVM_ED_FindFieldOffset("dimension_hit");
1370         prog->fieldoffsets.dimension_solid                = PRVM_ED_FindFieldOffset("dimension_solid");
1371         prog->fieldoffsets.disableclientprediction        = PRVM_ED_FindFieldOffset("disableclientprediction");
1372         prog->fieldoffsets.dphitcontentsmask              = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1373         prog->fieldoffsets.drawonlytoclient               = PRVM_ED_FindFieldOffset("drawonlytoclient");
1374         prog->fieldoffsets.exteriormodeltoclient          = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1375         prog->fieldoffsets.fatness                        = PRVM_ED_FindFieldOffset("fatness");
1376         prog->fieldoffsets.forceshader                    = PRVM_ED_FindFieldOffset("forceshader");
1377         prog->fieldoffsets.frame                          = PRVM_ED_FindFieldOffset("frame");
1378         prog->fieldoffsets.frame1time                     = PRVM_ED_FindFieldOffset("frame1time");
1379         prog->fieldoffsets.frame2                         = PRVM_ED_FindFieldOffset("frame2");
1380         prog->fieldoffsets.frame2time                     = PRVM_ED_FindFieldOffset("frame2time");
1381         prog->fieldoffsets.fullbright                     = PRVM_ED_FindFieldOffset("fullbright");
1382         prog->fieldoffsets.glow_color                     = PRVM_ED_FindFieldOffset("glow_color");
1383         prog->fieldoffsets.glow_size                      = PRVM_ED_FindFieldOffset("glow_size");
1384         prog->fieldoffsets.glow_trail                     = PRVM_ED_FindFieldOffset("glow_trail");
1385         prog->fieldoffsets.gravity                        = PRVM_ED_FindFieldOffset("gravity");
1386         prog->fieldoffsets.groundentity                   = PRVM_ED_FindFieldOffset("groundentity");
1387         prog->fieldoffsets.hull                           = PRVM_ED_FindFieldOffset("hull");
1388         prog->fieldoffsets.ideal_yaw                      = PRVM_ED_FindFieldOffset("ideal_yaw");
1389         prog->fieldoffsets.idealpitch                     = PRVM_ED_FindFieldOffset("idealpitch");
1390         prog->fieldoffsets.items2                         = PRVM_ED_FindFieldOffset("items2");
1391         prog->fieldoffsets.lerpfrac                       = PRVM_ED_FindFieldOffset("lerpfrac");
1392         prog->fieldoffsets.light_lev                      = PRVM_ED_FindFieldOffset("light_lev");
1393         prog->fieldoffsets.message                        = PRVM_ED_FindFieldOffset("message");
1394         prog->fieldoffsets.modelflags                     = PRVM_ED_FindFieldOffset("modelflags");
1395         prog->fieldoffsets.movement                       = PRVM_ED_FindFieldOffset("movement");
1396         prog->fieldoffsets.netaddress                     = PRVM_ED_FindFieldOffset("netaddress");
1397         prog->fieldoffsets.nextthink                      = PRVM_ED_FindFieldOffset("nextthink");
1398         prog->fieldoffsets.nodrawtoclient                 = PRVM_ED_FindFieldOffset("nodrawtoclient");
1399         prog->fieldoffsets.pflags                         = PRVM_ED_FindFieldOffset("pflags");
1400         prog->fieldoffsets.ping                           = PRVM_ED_FindFieldOffset("ping");
1401         prog->fieldoffsets.pitch_speed                    = PRVM_ED_FindFieldOffset("pitch_speed");
1402         prog->fieldoffsets.playermodel                    = PRVM_ED_FindFieldOffset("playermodel");
1403         prog->fieldoffsets.playerskin                     = PRVM_ED_FindFieldOffset("playerskin");
1404         prog->fieldoffsets.pmodel                         = PRVM_ED_FindFieldOffset("pmodel");
1405         prog->fieldoffsets.punchvector                    = PRVM_ED_FindFieldOffset("punchvector");
1406         prog->fieldoffsets.renderamt                      = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1407         prog->fieldoffsets.renderflags                    = PRVM_ED_FindFieldOffset("renderflags");
1408         prog->fieldoffsets.rendermode                     = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1409         prog->fieldoffsets.scale                          = PRVM_ED_FindFieldOffset("scale");
1410         prog->fieldoffsets.style                          = PRVM_ED_FindFieldOffset("style");
1411         prog->fieldoffsets.tag_entity                     = PRVM_ED_FindFieldOffset("tag_entity");
1412         prog->fieldoffsets.tag_index                      = PRVM_ED_FindFieldOffset("tag_index");
1413         prog->fieldoffsets.think                          = PRVM_ED_FindFieldOffset("think");
1414         prog->fieldoffsets.viewmodelforclient             = PRVM_ED_FindFieldOffset("viewmodelforclient");
1415         prog->fieldoffsets.viewzoom                       = PRVM_ED_FindFieldOffset("viewzoom");
1416         prog->fieldoffsets.yaw_speed                      = PRVM_ED_FindFieldOffset("yaw_speed");
1417         prog->funcoffsets.CSQC_ConsoleCommand             = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1418         prog->funcoffsets.CSQC_Ent_Remove                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1419         prog->funcoffsets.CSQC_Ent_Update                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1420         prog->funcoffsets.CSQC_Event                      = PRVM_ED_FindFunctionOffset("CSQC_Event");
1421         prog->funcoffsets.CSQC_Event_Sound                = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1422         prog->funcoffsets.CSQC_Init                       = PRVM_ED_FindFunctionOffset("CSQC_Init");
1423         prog->funcoffsets.CSQC_InputEvent                 = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1424         prog->funcoffsets.CSQC_Parse_CenterPrint          = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1425         prog->funcoffsets.CSQC_Parse_Print                = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1426         prog->funcoffsets.CSQC_Parse_StuffCmd             = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1427         prog->funcoffsets.CSQC_Parse_TempEntity           = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1428         prog->funcoffsets.CSQC_Shutdown                   = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1429         prog->funcoffsets.CSQC_UpdateView                 = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1430         prog->funcoffsets.EndFrame                        = PRVM_ED_FindFunctionOffset("EndFrame");
1431         prog->funcoffsets.RestoreGame                     = PRVM_ED_FindFunctionOffset("RestoreGame");
1432         prog->funcoffsets.SV_ChangeTeam                   = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1433         prog->funcoffsets.SV_ParseClientCommand           = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1434         prog->funcoffsets.SV_PlayerPhysics                = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1435         prog->funcoffsets.SV_OnEntityNoSpawnFunction      = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1436         prog->funcoffsets.GameCommand                     = PRVM_ED_FindFunctionOffset("GameCommand");
1437         prog->globaloffsets.SV_InitCmd                    = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1438         prog->globaloffsets.self                          = PRVM_ED_FindGlobalOffset("self");
1439         prog->globaloffsets.time                          = PRVM_ED_FindGlobalOffset("time");
1440         prog->globaloffsets.v_forward                     = PRVM_ED_FindGlobalOffset("v_forward");
1441         prog->globaloffsets.v_right                       = PRVM_ED_FindGlobalOffset("v_right");
1442         prog->globaloffsets.v_up                          = PRVM_ED_FindGlobalOffset("v_up");
1443         prog->globaloffsets.view_angles                   = PRVM_ED_FindGlobalOffset("view_angles");
1444         prog->globaloffsets.trace_allsolid                = PRVM_ED_FindGlobalOffset("trace_allsolid");
1445         prog->globaloffsets.trace_startsolid              = PRVM_ED_FindGlobalOffset("trace_startsolid");
1446         prog->globaloffsets.trace_fraction                = PRVM_ED_FindGlobalOffset("trace_fraction");
1447         prog->globaloffsets.trace_inwater                 = PRVM_ED_FindGlobalOffset("trace_inwater");
1448         prog->globaloffsets.trace_inopen                  = PRVM_ED_FindGlobalOffset("trace_inopen");
1449         prog->globaloffsets.trace_endpos                  = PRVM_ED_FindGlobalOffset("trace_endpos");
1450         prog->globaloffsets.trace_plane_normal            = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1451         prog->globaloffsets.trace_plane_dist              = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1452         prog->globaloffsets.trace_ent                     = PRVM_ED_FindGlobalOffset("trace_ent");
1453         prog->globaloffsets.trace_dphitcontents           = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1454         prog->globaloffsets.trace_dphitq3surfaceflags     = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1455         prog->globaloffsets.trace_dphittexturename        = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1456         prog->globaloffsets.trace_dpstartcontents         = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1457         prog->globaloffsets.intermission                  = PRVM_ED_FindGlobalOffset("intermission");
1458         prog->globaloffsets.coop                          = PRVM_ED_FindGlobalOffset("coop");
1459         prog->globaloffsets.deathmatch                    = PRVM_ED_FindGlobalOffset("deathmatch");
1460         prog->globaloffsets.dmg_take                      = PRVM_ED_FindGlobalOffset("dmg_take");
1461         prog->globaloffsets.dmg_save                      = PRVM_ED_FindGlobalOffset("dmg_save");
1462         prog->globaloffsets.dmg_origin                    = PRVM_ED_FindGlobalOffset("dmg_origin");
1463         prog->globaloffsets.sb_showscores                 = PRVM_ED_FindGlobalOffset("sb_showscores");
1464         prog->globaloffsets.drawfont                      = PRVM_ED_FindGlobalOffset("drawfont");
1465
1466         // menu qc only uses some functions, nothing else
1467         prog->funcoffsets.m_display                       = PRVM_ED_FindFunctionOffset("m_display");
1468         prog->funcoffsets.m_draw                          = PRVM_ED_FindFunctionOffset("m_draw");
1469         prog->funcoffsets.m_hide                          = PRVM_ED_FindFunctionOffset("m_hide");
1470         prog->funcoffsets.m_init                          = PRVM_ED_FindFunctionOffset("m_init");
1471         prog->funcoffsets.m_keydown                       = PRVM_ED_FindFunctionOffset("m_keydown");
1472         prog->funcoffsets.m_keyup                         = PRVM_ED_FindFunctionOffset("m_keyup");
1473         prog->funcoffsets.m_shutdown                      = PRVM_ED_FindFunctionOffset("m_shutdown");
1474         prog->funcoffsets.m_toggle                        = PRVM_ED_FindFunctionOffset("m_toggle");
1475 }
1476
1477 // not used
1478 /*
1479 typedef struct dpfield_s
1480 {
1481         int type;
1482         char *string;
1483 }
1484 dpfield_t;
1485
1486 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1487
1488 dpfield_t dpfields[] =
1489 {
1490 };
1491 */
1492
1493 /*
1494 ===============
1495 PRVM_ResetProg
1496 ===============
1497 */
1498
1499 void PRVM_ResetProg()
1500 {
1501         PRVM_GCALL(reset_cmd)();
1502         Mem_FreePool(&prog->progs_mempool);
1503         memset(prog,0,sizeof(prvm_prog_t));
1504         prog->starttime = Sys_DoubleTime();
1505 }
1506
1507 /*
1508 ===============
1509 PRVM_LoadLNO
1510 ===============
1511 */
1512 void PRVM_LoadLNO( const char *progname ) {
1513         fs_offset_t filesize;
1514         unsigned char *lno;
1515         unsigned int *header;
1516         char filename[512];
1517
1518         FS_StripExtension( progname, filename, sizeof( filename ) );
1519         strlcat( filename, ".lno", sizeof( filename ) );
1520
1521         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1522         if( !lno ) {
1523                 return;
1524         }
1525
1526 /*
1527 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
1528 <Spike>    SafeWrite (h, &version, sizeof(int));
1529 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
1530 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
1531 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
1532 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
1533 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1534 */
1535         if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1536                 Mem_Free(lno);
1537                 return;
1538         }
1539
1540         header = (unsigned int *) lno;
1541         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1542                 LittleLong( header[ 1 ] ) == 1 &&
1543                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1544                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1545                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1546                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1547         {
1548                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1549                 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1550         }
1551         Mem_Free( lno );
1552 }
1553
1554 /*
1555 ===============
1556 PRVM_LoadProgs
1557 ===============
1558 */
1559 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1560 {
1561         int i;
1562         dstatement_t *st;
1563         ddef_t *infielddefs;
1564         dfunction_t *dfunctions;
1565         fs_offset_t filesize;
1566
1567         if( prog->loaded ) {
1568                 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1569         }
1570
1571         prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1572         if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1573                 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1574
1575         Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1576
1577         prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1578
1579 // byte swap the header
1580         for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1581                 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1582
1583         if (prog->progs->version != PROG_VERSION)
1584                 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1585         if (prog->progs->crc != prog->headercrc)
1586                 PRVM_ERROR ("%s: %s system vars have been modified, progdefs.h is out of date", PRVM_NAME, filename);
1587
1588         //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1589         dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1590
1591         if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1592                 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1593         prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1594         prog->stringssize = prog->progs->numstrings;
1595
1596         prog->numknownstrings = 0;
1597         prog->maxknownstrings = 0;
1598         prog->knownstrings = NULL;
1599         prog->knownstrings_freeable = NULL;
1600
1601         prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1602
1603         // we need to expand the fielddefs list to include all the engine fields,
1604         // so allocate a new place for it
1605         infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1606         //                                                                                              ( + DPFIELDS                       )
1607         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1608
1609         prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1610
1611         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1612
1613         // moved edict_size calculation down below field adding code
1614
1615         //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1616         prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1617
1618 // byte swap the lumps
1619         for (i=0 ; i<prog->progs->numstatements ; i++)
1620         {
1621                 prog->statements[i].op = LittleShort(prog->statements[i].op);
1622                 prog->statements[i].a = LittleShort(prog->statements[i].a);
1623                 prog->statements[i].b = LittleShort(prog->statements[i].b);
1624                 prog->statements[i].c = LittleShort(prog->statements[i].c);
1625         }
1626
1627         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1628         for (i = 0;i < prog->progs->numfunctions;i++)
1629         {
1630                 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1631                 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1632                 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1633                 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1634                 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1635                 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1636                 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1637         }
1638
1639         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1640         {
1641                 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1642                 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1643                 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1644         }
1645
1646         // copy the progs fields to the new fields list
1647         for (i = 0;i < prog->progs->numfielddefs;i++)
1648         {
1649                 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1650                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1651                         PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1652                 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1653                 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1654         }
1655
1656         // append the required fields
1657         for (i = 0;i < (int) numrequiredfields;i++)
1658         {
1659                 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1660                 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1661                 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1662                 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1663                         prog->progs->entityfields += 3;
1664                 else
1665                         prog->progs->entityfields++;
1666                 prog->progs->numfielddefs++;
1667         }
1668
1669         // check required functions
1670         for(i=0 ; i < numrequiredfunc ; i++)
1671                 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1672                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1673
1674         // check required globals
1675         for(i=0 ; i < numrequiredglobals ; i++)
1676                 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1677                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1678
1679         for (i=0 ; i<prog->progs->numglobals ; i++)
1680                 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1681
1682         // moved edict_size calculation down here, below field adding code
1683         // LordHavoc: this no longer includes the prvm_edict_t header
1684         prog->edict_size = prog->progs->entityfields * 4;
1685         prog->edictareasize = prog->edict_size * prog->limit_edicts;
1686
1687         // LordHavoc: bounds check anything static
1688         for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1689         {
1690                 switch (st->op)
1691                 {
1692                 case OP_IF:
1693                 case OP_IFNOT:
1694                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1695                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1696                         break;
1697                 case OP_GOTO:
1698                         if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1699                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1700                         break;
1701                 // global global global
1702                 case OP_ADD_F:
1703                 case OP_ADD_V:
1704                 case OP_SUB_F:
1705                 case OP_SUB_V:
1706                 case OP_MUL_F:
1707                 case OP_MUL_V:
1708                 case OP_MUL_FV:
1709                 case OP_MUL_VF:
1710                 case OP_DIV_F:
1711                 case OP_BITAND:
1712                 case OP_BITOR:
1713                 case OP_GE:
1714                 case OP_LE:
1715                 case OP_GT:
1716                 case OP_LT:
1717                 case OP_AND:
1718                 case OP_OR:
1719                 case OP_EQ_F:
1720                 case OP_EQ_V:
1721                 case OP_EQ_S:
1722                 case OP_EQ_E:
1723                 case OP_EQ_FNC:
1724                 case OP_NE_F:
1725                 case OP_NE_V:
1726                 case OP_NE_S:
1727                 case OP_NE_E:
1728                 case OP_NE_FNC:
1729                 case OP_ADDRESS:
1730                 case OP_LOAD_F:
1731                 case OP_LOAD_FLD:
1732                 case OP_LOAD_ENT:
1733                 case OP_LOAD_S:
1734                 case OP_LOAD_FNC:
1735                 case OP_LOAD_V:
1736                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1737                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1738                         break;
1739                 // global none global
1740                 case OP_NOT_F:
1741                 case OP_NOT_V:
1742                 case OP_NOT_S:
1743                 case OP_NOT_FNC:
1744                 case OP_NOT_ENT:
1745                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1746                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1747                         break;
1748                 // 2 globals
1749                 case OP_STOREP_F:
1750                 case OP_STOREP_ENT:
1751                 case OP_STOREP_FLD:
1752                 case OP_STOREP_S:
1753                 case OP_STOREP_FNC:
1754                 case OP_STORE_F:
1755                 case OP_STORE_ENT:
1756                 case OP_STORE_FLD:
1757                 case OP_STORE_S:
1758                 case OP_STORE_FNC:
1759                 case OP_STATE:
1760                 case OP_STOREP_V:
1761                 case OP_STORE_V:
1762                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1763                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1764                         break;
1765                 // 1 global
1766                 case OP_CALL0:
1767                 case OP_CALL1:
1768                 case OP_CALL2:
1769                 case OP_CALL3:
1770                 case OP_CALL4:
1771                 case OP_CALL5:
1772                 case OP_CALL6:
1773                 case OP_CALL7:
1774                 case OP_CALL8:
1775                 case OP_DONE:
1776                 case OP_RETURN:
1777                         if ((unsigned short) st->a >= prog->progs->numglobals)
1778                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1779                         break;
1780                 default:
1781                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
1782                         break;
1783                 }
1784         }
1785
1786         PRVM_LoadLNO(filename);
1787
1788         PRVM_Init_Exec();
1789
1790         prog->loaded = TRUE;
1791
1792         // set flags & ddef_ts in prog
1793
1794         prog->flag = 0;
1795
1796         PRVM_FindOffsets();
1797
1798         PRVM_GCALL(init_cmd)();
1799
1800         // init mempools
1801         PRVM_MEM_Alloc();
1802 }
1803
1804
1805 void PRVM_Fields_f (void)
1806 {
1807         int i, j, ednum, used, usedamount;
1808         int *counts;
1809         char tempstring[MAX_INPUTLINE], tempstring2[260];
1810         const char *name;
1811         prvm_edict_t *ed;
1812         ddef_t *d;
1813         int *v;
1814
1815         // TODO
1816         /*
1817         if (!sv.active)
1818         {
1819                 Con_Print("no progs loaded\n");
1820                 return;
1821         }
1822         */
1823
1824         if(Cmd_Argc() != 2)
1825         {
1826                 Con_Print("prvm_fields <program name>\n");
1827                 return;
1828         }
1829
1830         PRVM_Begin;
1831         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1832                 return;
1833
1834         counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1835         for (ednum = 0;ednum < prog->max_edicts;ednum++)
1836         {
1837                 ed = PRVM_EDICT_NUM(ednum);
1838                 if (ed->priv.required->free)
1839                         continue;
1840                 for (i = 1;i < prog->progs->numfielddefs;i++)
1841                 {
1842                         d = &prog->fielddefs[i];
1843                         name = PRVM_GetString(d->s_name);
1844                         if (name[strlen(name)-2] == '_')
1845                                 continue;       // skip _x, _y, _z vars
1846                         v = (int *)((char *)ed->fields.vp + d->ofs*4);
1847                         // if the value is still all 0, skip the field
1848                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1849                         {
1850                                 if (v[j])
1851                                 {
1852                                         counts[i]++;
1853                                         break;
1854                                 }
1855                         }
1856                 }
1857         }
1858         used = 0;
1859         usedamount = 0;
1860         tempstring[0] = 0;
1861         for (i = 0;i < prog->progs->numfielddefs;i++)
1862         {
1863                 d = &prog->fielddefs[i];
1864                 name = PRVM_GetString(d->s_name);
1865                 if (name[strlen(name)-2] == '_')
1866                         continue;       // skip _x, _y, _z vars
1867                 switch(d->type & ~DEF_SAVEGLOBAL)
1868                 {
1869                 case ev_string:
1870                         strlcat(tempstring, "string   ", sizeof(tempstring));
1871                         break;
1872                 case ev_entity:
1873                         strlcat(tempstring, "entity   ", sizeof(tempstring));
1874                         break;
1875                 case ev_function:
1876                         strlcat(tempstring, "function ", sizeof(tempstring));
1877                         break;
1878                 case ev_field:
1879                         strlcat(tempstring, "field    ", sizeof(tempstring));
1880                         break;
1881                 case ev_void:
1882                         strlcat(tempstring, "void     ", sizeof(tempstring));
1883                         break;
1884                 case ev_float:
1885                         strlcat(tempstring, "float    ", sizeof(tempstring));
1886                         break;
1887                 case ev_vector:
1888                         strlcat(tempstring, "vector   ", sizeof(tempstring));
1889                         break;
1890                 case ev_pointer:
1891                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
1892                         break;
1893                 default:
1894                         sprintf (tempstring2, "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
1895                         strlcat(tempstring, tempstring2, sizeof(tempstring));
1896                         break;
1897                 }
1898                 if (strlen(name) > sizeof(tempstring2)-4)
1899                 {
1900                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
1901                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
1902                         tempstring2[sizeof(tempstring2)-1] = 0;
1903                         name = tempstring2;
1904                 }
1905                 strlcat(tempstring, name, sizeof(tempstring));
1906                 for (j = (int)strlen(name);j < 25;j++)
1907                         strlcat(tempstring, " ", sizeof(tempstring));
1908                 sprintf(tempstring2, "%5d", counts[i]);
1909                 strlcat(tempstring, tempstring2, sizeof(tempstring));
1910                 strlcat(tempstring, "\n", sizeof(tempstring));
1911                 if (strlen(tempstring) >= sizeof(tempstring)/2)
1912                 {
1913                         Con_Print(tempstring);
1914                         tempstring[0] = 0;
1915                 }
1916                 if (counts[i])
1917                 {
1918                         used++;
1919                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
1920                 }
1921         }
1922         Mem_Free(counts);
1923         Con_Printf("%s: %i entity fields (%i in use), totalling %i bytes per edict (%i in use), %i edicts allocated, %i bytes total spent on edict fields (%i needed)\n", PRVM_NAME, prog->progs->entityfields, used, prog->progs->entityfields * 4, usedamount * 4, prog->max_edicts, prog->progs->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
1924
1925         PRVM_End;
1926 }
1927
1928 void PRVM_Globals_f (void)
1929 {
1930         int i;
1931         const char *wildcard;
1932         int numculled;
1933                 numculled = 0;
1934         // TODO
1935         /*if (!sv.active)
1936         {
1937                 Con_Print("no progs loaded\n");
1938                 return;
1939         }*/
1940         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
1941         {
1942                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
1943                 return;
1944         }
1945
1946         PRVM_Begin;
1947         if(!PRVM_SetProgFromString (Cmd_Argv (1)))
1948                 return;
1949
1950         if( Cmd_Argc() == 3)
1951                 wildcard = Cmd_Argv(2);
1952         else
1953                 wildcard = NULL;
1954
1955         Con_Printf("%s :", PRVM_NAME);
1956
1957         for (i = 0;i < prog->progs->numglobaldefs;i++)
1958         {
1959                 if(wildcard)
1960                         if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
1961                         {
1962                                 numculled++;
1963                                 continue;
1964                         }
1965                 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
1966         }
1967         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
1968
1969         PRVM_End;
1970 }
1971
1972 /*
1973 ===============
1974 PRVM_Global
1975 ===============
1976 */
1977 void PRVM_Global_f(void)
1978 {
1979         ddef_t *global;
1980         if( Cmd_Argc() != 3 ) {
1981                 Con_Printf( "prvm_global <program name> <global name>\n" );
1982                 return;
1983         }
1984
1985         PRVM_Begin;
1986         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
1987                 return;
1988
1989         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
1990         if( !global )
1991                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1992         else
1993                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
1994         PRVM_End;
1995 }
1996
1997 /*
1998 ===============
1999 PRVM_GlobalSet
2000 ===============
2001 */
2002 void PRVM_GlobalSet_f(void)
2003 {
2004         ddef_t *global;
2005         if( Cmd_Argc() != 4 ) {
2006                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2007                 return;
2008         }
2009
2010         PRVM_Begin;
2011         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2012                 return;
2013
2014         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2015         if( !global )
2016                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2017         else
2018                 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3) );
2019         PRVM_End;
2020 }
2021
2022 /*
2023 ===============
2024 PRVM_Init
2025 ===============
2026 */
2027 void PRVM_Init (void)
2028 {
2029         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2030         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2031         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2032         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2033         Cmd_AddCommand ("prvm_callprofile", PRVM_CallProfile_f, "prints execution statistics about the most time consuming QuakeC calls from the engine in the selected VM (server, client, menu)");
2034         Cmd_AddCommand ("prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2035         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2036         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2037         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2038         Cmd_AddCommand ("prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2039         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2040         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2041         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2042         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2043         // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
2044         Cvar_RegisterVariable (&prvm_boundscheck);
2045         Cvar_RegisterVariable (&prvm_traceqc);
2046         Cvar_RegisterVariable (&prvm_statementprofiling);
2047
2048         //VM_Cmd_Init();
2049 }
2050
2051 /*
2052 ===============
2053 PRVM_InitProg
2054 ===============
2055 */
2056 void PRVM_InitProg(int prognr)
2057 {
2058         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2059                 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2060
2061         prog = &prog_list[prognr];
2062
2063         if(prog->loaded)
2064                 PRVM_ResetProg();
2065
2066         memset(prog, 0, sizeof(prvm_prog_t));
2067         prog->starttime = Sys_DoubleTime();
2068
2069         prog->error_cmd = Host_Error;
2070 }
2071
2072 int PRVM_GetProgNr()
2073 {
2074         return prog - prog_list;
2075 }
2076
2077 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2078 {
2079         return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2080 }
2081
2082 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2083 {
2084         _Mem_Free(buffer, filename, fileline);
2085 }
2086
2087 void _PRVM_FreeAll(const char *filename, int fileline)
2088 {
2089         prog->progs = NULL;
2090         prog->fielddefs = NULL;
2091         prog->functions = NULL;
2092         _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2093 }
2094
2095 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2096 prvm_edict_t *PRVM_EDICT_NUM_ERROR(int n, char *filename, int fileline)
2097 {
2098         PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2099         return NULL;
2100 }
2101
2102 /*
2103 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2104 {
2105         PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2106         return 0;
2107 }
2108
2109 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2110 {
2111         int n;
2112         n = e - prog->edicts;
2113         if ((unsigned int)n >= prog->limit_edicts)
2114                 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2115         return n;
2116 }
2117
2118 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2119 //{
2120 //      return e - prog->edicts;
2121 //}
2122
2123 //#define       PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2124 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2125 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2126 {
2127         int n;
2128         n = e - prog->edicts;
2129         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2130                 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2131         return n;// EXPERIMENTAL
2132         //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2133 }
2134 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2135 {
2136         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2137                 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2138         return prog->edicts + n; // EXPERIMENTAL
2139         //return prog->edicts + ((n) / (progs->entityfields * 4));
2140 }
2141 */
2142
2143
2144 sizebuf_t vm_tempstringsbuf;
2145
2146 const char *PRVM_GetString(int num)
2147 {
2148         if (num >= 0)
2149         {
2150                 if (num < prog->stringssize)
2151                         return prog->strings + num;
2152                 else
2153 #if 1
2154                 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2155                 {
2156                         num -= prog->stringssize;
2157                         if (num < vm_tempstringsbuf.cursize)
2158                                 return (char *)vm_tempstringsbuf.data + num;
2159                         else
2160                         {
2161                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)", num, vm_tempstringsbuf.cursize);
2162                                 return "";
2163                         }
2164                 }
2165                 else
2166 #endif
2167                 {
2168                         VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)", num, prog->stringssize);
2169                         return "";
2170                 }
2171         }
2172         else
2173         {
2174                 num = -1 - num;
2175 #if 0
2176                 if (num >= (1<<30))
2177                 {
2178                         // special range reserved for tempstrings
2179                         num -= (1<<30);
2180                         if (num < vm_tempstringsbuf.cursize)
2181                                 return (char *)vm_tempstringsbuf.data + num;
2182                         else
2183                         {
2184                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)", num, vm_tempstringsbuf.cursize);
2185                                 return "";
2186                         }
2187                 }
2188                 else
2189 #endif
2190                 if (num < prog->numknownstrings)
2191                 {
2192                         if (!prog->knownstrings[num])
2193                                 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)", num);
2194                         return prog->knownstrings[num];
2195                 }
2196                 else
2197                 {
2198                         VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)", num, prog->numknownstrings);
2199                         return "";
2200                 }
2201         }
2202 }
2203
2204 int PRVM_SetEngineString(const char *s)
2205 {
2206         int i;
2207         if (!s)
2208                 return 0;
2209         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2210                 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2211         // if it's in the tempstrings area, use a reserved range
2212         // (otherwise we'd get millions of useless string offsets cluttering the database)
2213         if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2214 #if 1
2215                 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2216 #else
2217                 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2218 #endif
2219         // see if it's a known string address
2220         for (i = 0;i < prog->numknownstrings;i++)
2221                 if (prog->knownstrings[i] == s)
2222                         return -1 - i;
2223         // new unknown engine string
2224         if (developer.integer >= 200)
2225                 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2226         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2227                 if (!prog->knownstrings[i])
2228                         break;
2229         if (i >= prog->numknownstrings)
2230         {
2231                 if (i >= prog->maxknownstrings)
2232                 {
2233                         const char **oldstrings = prog->knownstrings;
2234                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2235                         prog->maxknownstrings += 128;
2236                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2237                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2238                         if (prog->numknownstrings)
2239                         {
2240                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2241                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2242                         }
2243                 }
2244                 prog->numknownstrings++;
2245         }
2246         prog->firstfreeknownstring = i + 1;
2247         prog->knownstrings[i] = s;
2248         return -1 - i;
2249 }
2250
2251 // temp string handling
2252
2253 // all tempstrings go into this buffer consecutively, and it is reset
2254 // whenever PRVM_ExecuteProgram returns to the engine
2255 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2256 //  restores it on return, so multiple recursive calls can share the same
2257 //  buffer)
2258 // the buffer size is automatically grown as needed
2259
2260 int PRVM_SetTempString(const char *s)
2261 {
2262         int size;
2263         char *t;
2264         if (!s)
2265                 return 0;
2266         size = (int)strlen(s) + 1;
2267         if (developer.integer >= 300)
2268                 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2269         if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2270         {
2271                 sizebuf_t old = vm_tempstringsbuf;
2272                 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2273                         PRVM_ERROR("PRVM_SetTempString: ran out of tempstring memory!  (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", vm_tempstringsbuf.cursize, size);
2274                 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2275                 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2276                         vm_tempstringsbuf.maxsize *= 2;
2277                 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2278                 {
2279                         if (developer.integer >= 100)
2280                                 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2281                         vm_tempstringsbuf.data = Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2282                         if (old.cursize)
2283                                 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2284                         if (old.data)
2285                                 Mem_Free(old.data);
2286                 }
2287         }
2288         t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2289         memcpy(t, s, size);
2290         vm_tempstringsbuf.cursize += size;
2291         return PRVM_SetEngineString(t);
2292 }
2293
2294 int PRVM_AllocString(size_t bufferlength, char **pointer)
2295 {
2296         int i;
2297         if (!bufferlength)
2298                 return 0;
2299         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2300                 if (!prog->knownstrings[i])
2301                         break;
2302         if (i >= prog->numknownstrings)
2303         {
2304                 if (i >= prog->maxknownstrings)
2305                 {
2306                         const char **oldstrings = prog->knownstrings;
2307                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2308                         prog->maxknownstrings += 128;
2309                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2310                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2311                         if (prog->numknownstrings)
2312                         {
2313                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2314                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2315                         }
2316                 }
2317                 prog->numknownstrings++;
2318         }
2319         prog->firstfreeknownstring = i + 1;
2320         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2321         prog->knownstrings_freeable[i] = true;
2322         if (pointer)
2323                 *pointer = (char *)(prog->knownstrings[i]);
2324         return -1 - i;
2325 }
2326
2327 void PRVM_FreeString(int num)
2328 {
2329         if (num == 0)
2330                 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2331         else if (num >= 0 && num < prog->stringssize)
2332                 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2333         else if (num < 0 && num >= -prog->numknownstrings)
2334         {
2335                 num = -1 - num;
2336                 if (!prog->knownstrings[num])
2337                         PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2338                 if (!prog->knownstrings[num])
2339                         PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2340                 PRVM_Free((char *)prog->knownstrings[num]);
2341                 prog->knownstrings[num] = NULL;
2342                 prog->knownstrings_freeable[num] = false;
2343                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2344         }
2345         else
2346                 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2347 }
2348