added prvm_startupreuseedicttime cvar
[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, qboolean parsebackslash);
33
34 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
35 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
36 // LordHavoc: counts usage of each QuakeC statement
37 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)"};
38 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
39 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
40 cvar_t prvm_leaktest_ignore_classnames = {0, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
41 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
42 cvar_t prvm_startupreuseedicttime = {0, "prvm_startupreuseedicttime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
43
44 qboolean prvm_runawaycheck = true;
45
46 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
47 // 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)
48 qboolean prvm_boundscheck = true;
49
50 extern sizebuf_t vm_tempstringsbuf;
51
52 //============================================================================
53 // mempool handling
54
55 /*
56 ===============
57 PRVM_MEM_Alloc
58 ===============
59 */
60 void PRVM_MEM_Alloc(void)
61 {
62         int i;
63
64         // reserve space for the null entity aka world
65         // check bound of max_edicts
66         prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
67         prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
68
69         // edictprivate_size has to be min as big prvm_edict_private_t
70         prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
71
72         // alloc edicts
73         prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
74
75         // alloc edict private space
76         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
77
78         // alloc edict fields
79         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
80
81         // set edict pointers
82         for(i = 0; i < prog->max_edicts; i++)
83         {
84                 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
85                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
86         }
87 }
88
89 /*
90 ===============
91 PRVM_MEM_IncreaseEdicts
92 ===============
93 */
94 void PRVM_MEM_IncreaseEdicts(void)
95 {
96         int             i;
97         int             oldmaxedicts = prog->max_edicts;
98         void    *oldedictsfields = prog->edictsfields;
99         void    *oldedictprivate = prog->edictprivate;
100
101         if(prog->max_edicts >= prog->limit_edicts)
102                 return;
103
104         PRVM_GCALL(begin_increase_edicts)();
105
106         // increase edicts
107         prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
108
109         prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
110         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
111
112         memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
113         memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
114
115         //set e and v pointers
116         for(i = 0; i < prog->max_edicts; i++)
117         {
118                 prog->edicts[i].priv.required  = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
119                 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
120         }
121
122         PRVM_GCALL(end_increase_edicts)();
123
124         Mem_Free(oldedictsfields);
125         Mem_Free(oldedictprivate);
126 }
127
128 //============================================================================
129 // normal prvm
130
131 int PRVM_ED_FindFieldOffset(const char *field)
132 {
133         ddef_t *d;
134         d = PRVM_ED_FindField(field);
135         if (!d)
136                 return -1;
137         return d->ofs;
138 }
139
140 int PRVM_ED_FindGlobalOffset(const char *global)
141 {
142         ddef_t *d;
143         d = PRVM_ED_FindGlobal(global);
144         if (!d)
145                 return -1;
146         return d->ofs;
147 }
148
149 func_t PRVM_ED_FindFunctionOffset(const char *function)
150 {
151         mfunction_t *f;
152         f = PRVM_ED_FindFunction(function);
153         if (!f)
154                 return 0;
155         return (func_t)(f - prog->functions);
156 }
157
158 qboolean PRVM_ProgLoaded(int prognr)
159 {
160         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
161                 return FALSE;
162
163         return (prog_list[prognr].loaded ? TRUE : FALSE);
164 }
165
166 /*
167 =================
168 PRVM_SetProgFromString
169 =================
170 */
171 // perhaps add a return value when the str doesnt exist
172 qboolean PRVM_SetProgFromString(const char *str)
173 {
174         int i = 0;
175         for(; i < PRVM_MAXPROGS ; i++)
176                 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
177                 {
178                         if(prog_list[i].loaded)
179                         {
180                                 prog = &prog_list[i];
181                                 return TRUE;
182                         }
183                         else
184                         {
185                                 Con_Printf("%s not loaded !\n",PRVM_NAME);
186                                 return FALSE;
187                         }
188                 }
189
190         Con_Printf("Invalid program name %s !\n", str);
191         return FALSE;
192 }
193
194 /*
195 =================
196 PRVM_SetProg
197 =================
198 */
199 void PRVM_SetProg(int prognr)
200 {
201         if(0 <= prognr && prognr < PRVM_MAXPROGS)
202         {
203                 if(prog_list[prognr].loaded)
204                         prog = &prog_list[prognr];
205                 else
206                         PRVM_ERROR("%i not loaded !", prognr);
207                 return;
208         }
209         PRVM_ERROR("Invalid program number %i", prognr);
210 }
211
212 /*
213 =================
214 PRVM_ED_ClearEdict
215
216 Sets everything to NULL
217 =================
218 */
219 void PRVM_ED_ClearEdict (prvm_edict_t *e)
220 {
221         memset (e->fields.vp, 0, prog->progs->entityfields * 4);
222         e->priv.required->free = false;
223
224         // AK: Let the init_edict function determine if something needs to be initialized
225         PRVM_GCALL(init_edict)(e);
226 }
227
228 const char *PRVM_AllocationOrigin(void)
229 {
230         char *buf = NULL;
231         if(prog->leaktest_active)
232         if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
233         {
234                 buf = (char *)PRVM_Alloc(128);
235                 PRVM_ShortStackTrace(buf, 128);
236         }
237         return buf;
238 }
239
240 /*
241 =================
242 PRVM_ED_CanAlloc
243
244 Returns if this particular edict could get allocated by PRVM_ED_Alloc
245 =================
246 */
247 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
248 {
249         if(!e->priv.required->free)
250                 return false;
251         if(e->priv.required->freetime < prog->starttime + prvm_startupreuseedicttime.value)
252                 return true;
253         if(realtime > e->priv.required->freetime + 1)
254                 return true;
255         return false; // entity slot still blocked because the entity was freed less than one second ago
256 }
257
258 /*
259 =================
260 PRVM_ED_Alloc
261
262 Either finds a free edict, or allocates a new one.
263 Try to avoid reusing an entity that was recently freed, because it
264 can cause the client to think the entity morphed into something else
265 instead of being removed and recreated, which can cause interpolated
266 angles and bad trails.
267 =================
268 */
269 prvm_edict_t *PRVM_ED_Alloc (void)
270 {
271         int                     i;
272         prvm_edict_t            *e;
273
274         // the client qc dont need maxclients
275         // thus it doesnt need to use svs.maxclients
276         // AK:  changed i=svs.maxclients+1
277         // AK:  changed so the edict 0 wont spawn -> used as reserved/world entity
278         //              although the menu/client has no world
279         for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
280         {
281                 e = PRVM_EDICT_NUM(i);
282                 if(PRVM_ED_CanAlloc(e))
283                 {
284                         PRVM_ED_ClearEdict (e);
285                         e->priv.required->allocation_origin = PRVM_AllocationOrigin();
286                         return e;
287                 }
288         }
289
290         if (i == prog->limit_edicts)
291                 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
292
293         prog->num_edicts++;
294         if (prog->num_edicts >= prog->max_edicts)
295                 PRVM_MEM_IncreaseEdicts();
296
297         e = PRVM_EDICT_NUM(i);
298         PRVM_ED_ClearEdict (e);
299
300         e->priv.required->allocation_origin = PRVM_AllocationOrigin();
301
302         return e;
303 }
304
305 /*
306 =================
307 PRVM_ED_Free
308
309 Marks the edict as free
310 FIXME: walk all entities and NULL out references to this entity
311 =================
312 */
313 void PRVM_ED_Free (prvm_edict_t *ed)
314 {
315         // dont delete the null entity (world) or reserved edicts
316         if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
317                 return;
318
319         PRVM_GCALL(free_edict)(ed);
320
321         ed->priv.required->free = true;
322         ed->priv.required->freetime = realtime;
323         if(ed->priv.required->allocation_origin)
324         {
325                 PRVM_Free((char *)ed->priv.required->allocation_origin);
326                 ed->priv.required->allocation_origin = NULL;
327         }
328 }
329
330 //===========================================================================
331
332 /*
333 ============
334 PRVM_ED_GlobalAtOfs
335 ============
336 */
337 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
338 {
339         ddef_t          *def;
340         int                     i;
341
342         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
343         {
344                 def = &prog->globaldefs[i];
345                 if (def->ofs == ofs)
346                         return def;
347         }
348         return NULL;
349 }
350
351 /*
352 ============
353 PRVM_ED_FieldAtOfs
354 ============
355 */
356 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
357 {
358         ddef_t          *def;
359         int                     i;
360
361         for (i=0 ; i<prog->progs->numfielddefs ; i++)
362         {
363                 def = &prog->fielddefs[i];
364                 if (def->ofs == ofs)
365                         return def;
366         }
367         return NULL;
368 }
369
370 /*
371 ============
372 PRVM_ED_FindField
373 ============
374 */
375 ddef_t *PRVM_ED_FindField (const char *name)
376 {
377         ddef_t *def;
378         int i;
379
380         for (i=0 ; i<prog->progs->numfielddefs ; i++)
381         {
382                 def = &prog->fielddefs[i];
383                 if (!strcmp(PRVM_GetString(def->s_name), name))
384                         return def;
385         }
386         return NULL;
387 }
388
389 /*
390 ============
391 PRVM_ED_FindGlobal
392 ============
393 */
394 ddef_t *PRVM_ED_FindGlobal (const char *name)
395 {
396         ddef_t *def;
397         int i;
398
399         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
400         {
401                 def = &prog->globaldefs[i];
402                 if (!strcmp(PRVM_GetString(def->s_name), name))
403                         return def;
404         }
405         return NULL;
406 }
407
408
409 /*
410 ============
411 PRVM_ED_FindFunction
412 ============
413 */
414 mfunction_t *PRVM_ED_FindFunction (const char *name)
415 {
416         mfunction_t             *func;
417         int                             i;
418
419         for (i=0 ; i<prog->progs->numfunctions ; i++)
420         {
421                 func = &prog->functions[i];
422                 if (!strcmp(PRVM_GetString(func->s_name), name))
423                         return func;
424         }
425         return NULL;
426 }
427
428
429 /*
430 ============
431 PRVM_ValueString
432
433 Returns a string describing *data in a type specific manner
434 =============
435 */
436 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
437 {
438         static char line[MAX_INPUTLINE];
439         ddef_t *def;
440         mfunction_t *f;
441         int n;
442
443         type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
444
445         switch (type)
446         {
447         case ev_string:
448                 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
449                 break;
450         case ev_entity:
451                 n = val->edict;
452                 if (n < 0 || n >= prog->limit_edicts)
453                         dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
454                 else
455                         dpsnprintf (line, sizeof(line), "entity %i", n);
456                 break;
457         case ev_function:
458                 f = prog->functions + val->function;
459                 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
460                 break;
461         case ev_field:
462                 def = PRVM_ED_FieldAtOfs ( val->_int );
463                 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
464                 break;
465         case ev_void:
466                 dpsnprintf (line, sizeof(line), "void");
467                 break;
468         case ev_float:
469                 // LordHavoc: changed from %5.1f to %10.4f
470                 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
471                 break;
472         case ev_vector:
473                 // LordHavoc: changed from %5.1f to %10.4f
474                 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
475                 break;
476         case ev_pointer:
477                 dpsnprintf (line, sizeof(line), "pointer");
478                 break;
479         default:
480                 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
481                 break;
482         }
483
484         return line;
485 }
486
487 /*
488 ============
489 PRVM_UglyValueString
490
491 Returns a string describing *data in a type specific manner
492 Easier to parse than PR_ValueString
493 =============
494 */
495 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
496 {
497         static char line[MAX_INPUTLINE];
498         int i;
499         const char *s;
500         ddef_t *def;
501         mfunction_t *f;
502
503         type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
504
505         switch (type)
506         {
507         case ev_string:
508                 // Parse the string a bit to turn special characters
509                 // (like newline, specifically) into escape codes,
510                 // this fixes saving games from various mods
511                 s = PRVM_GetString (val->string);
512                 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
513                 {
514                         if (*s == '\n')
515                         {
516                                 line[i++] = '\\';
517                                 line[i++] = 'n';
518                         }
519                         else if (*s == '\r')
520                         {
521                                 line[i++] = '\\';
522                                 line[i++] = 'r';
523                         }
524                         else if (*s == '\\')
525                         {
526                                 line[i++] = '\\';
527                                 line[i++] = '\\';
528                         }
529                         else if (*s == '"')
530                         {
531                                 line[i++] = '\\';
532                                 line[i++] = '"';
533                         }
534                         else
535                                 line[i++] = *s;
536                         s++;
537                 }
538                 line[i] = '\0';
539                 break;
540         case ev_entity:
541                 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
542                 break;
543         case ev_function:
544                 f = prog->functions + val->function;
545                 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
546                 break;
547         case ev_field:
548                 def = PRVM_ED_FieldAtOfs ( val->_int );
549                 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
550                 break;
551         case ev_void:
552                 dpsnprintf (line, sizeof (line), "void");
553                 break;
554         case ev_float:
555                 dpsnprintf (line, sizeof (line), "%f", val->_float);
556                 break;
557         case ev_vector:
558                 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
559                 break;
560         default:
561                 dpsnprintf (line, sizeof (line), "bad type %i", type);
562                 break;
563         }
564
565         return line;
566 }
567
568 /*
569 ============
570 PRVM_GlobalString
571
572 Returns a string with a description and the contents of a global,
573 padded to 20 field width
574 ============
575 */
576 char *PRVM_GlobalString (int ofs)
577 {
578         char    *s;
579         //size_t        i;
580         ddef_t  *def;
581         void    *val;
582         static char     line[128];
583
584         val = (void *)&prog->globals.generic[ofs];
585         def = PRVM_ED_GlobalAtOfs(ofs);
586         if (!def)
587                 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
588         else
589         {
590                 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
591                 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
592         }
593
594         //i = strlen(line);
595         //for ( ; i<20 ; i++)
596         //      strcat (line," ");
597         //strcat (line," ");
598
599         return line;
600 }
601
602 char *PRVM_GlobalStringNoContents (int ofs)
603 {
604         //size_t        i;
605         ddef_t  *def;
606         static char     line[128];
607
608         def = PRVM_ED_GlobalAtOfs(ofs);
609         if (!def)
610                 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
611         else
612                 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
613
614         //i = strlen(line);
615         //for ( ; i<20 ; i++)
616         //      strcat (line," ");
617         //strcat (line," ");
618
619         return line;
620 }
621
622
623 /*
624 =============
625 PRVM_ED_Print
626
627 For debugging
628 =============
629 */
630 // LordHavoc: optimized this to print out much more quickly (tempstring)
631 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
632 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
633 {
634         size_t  l;
635         ddef_t  *d;
636         int             *v;
637         int             i, j;
638         const char      *name;
639         int             type;
640         char    tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
641
642         if (ed->priv.required->free)
643         {
644                 Con_Printf("%s: FREE\n",PRVM_NAME);
645                 return;
646         }
647
648         tempstring[0] = 0;
649         dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
650         for (i=1 ; i<prog->progs->numfielddefs ; i++)
651         {
652                 d = &prog->fielddefs[i];
653                 name = PRVM_GetString(d->s_name);
654                 if (name[strlen(name)-2] == '_')
655                         continue;       // skip _x, _y, _z vars
656
657                 // Check Field Name Wildcard
658                 if(wildcard_fieldname)
659                         if( !matchpattern(name, wildcard_fieldname, 1) )
660                                 // Didn't match; skip
661                                 continue;
662
663                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
664
665         // if the value is still all 0, skip the field
666                 type = d->type & ~DEF_SAVEGLOBAL;
667
668                 for (j=0 ; j<prvm_type_size[type] ; j++)
669                         if (v[j])
670                                 break;
671                 if (j == prvm_type_size[type])
672                         continue;
673
674                 if (strlen(name) > sizeof(tempstring2)-4)
675                 {
676                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
677                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
678                         tempstring2[sizeof(tempstring2)-1] = 0;
679                         name = tempstring2;
680                 }
681                 strlcat(tempstring, name, sizeof(tempstring));
682                 for (l = strlen(name);l < 14;l++)
683                         strlcat(tempstring, " ", sizeof(tempstring));
684                 strlcat(tempstring, " ", sizeof(tempstring));
685
686                 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
687                 if (strlen(name) > sizeof(tempstring2)-4)
688                 {
689                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
690                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
691                         tempstring2[sizeof(tempstring2)-1] = 0;
692                         name = tempstring2;
693                 }
694                 strlcat(tempstring, name, sizeof(tempstring));
695                 strlcat(tempstring, "\n", sizeof(tempstring));
696                 if (strlen(tempstring) >= sizeof(tempstring)/2)
697                 {
698                         Con_Print(tempstring);
699                         tempstring[0] = 0;
700                 }
701         }
702         if (tempstring[0])
703                 Con_Print(tempstring);
704 }
705
706 /*
707 =============
708 PRVM_ED_Write
709
710 For savegames
711 =============
712 */
713 extern cvar_t developer_entityparsing;
714 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
715 {
716         ddef_t  *d;
717         int             *v;
718         int             i, j;
719         const char      *name;
720         int             type;
721
722         FS_Print(f, "{\n");
723
724         if (ed->priv.required->free)
725         {
726                 FS_Print(f, "}\n");
727                 return;
728         }
729
730         for (i=1 ; i<prog->progs->numfielddefs ; i++)
731         {
732                 d = &prog->fielddefs[i];
733                 name = PRVM_GetString(d->s_name);
734
735                 if(developer_entityparsing.integer)
736                         Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
737
738                 if (name[strlen(name)-2] == '_')
739                         continue;       // skip _x, _y, _z vars
740
741                 v = (int *)((char *)ed->fields.vp + d->ofs*4);
742
743         // if the value is still all 0, skip the field
744                 type = d->type & ~DEF_SAVEGLOBAL;
745                 for (j=0 ; j<prvm_type_size[type] ; j++)
746                         if (v[j])
747                                 break;
748                 if (j == prvm_type_size[type])
749                         continue;
750
751                 FS_Printf(f,"\"%s\" ",name);
752                 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
753                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
754                 prog->statestring = NULL;
755         }
756
757         FS_Print(f, "}\n");
758 }
759
760 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
761 {
762         PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
763 }
764
765 /*
766 =============
767 PRVM_ED_PrintEdicts_f
768
769 For debugging, prints all the entities in the current server
770 =============
771 */
772 void PRVM_ED_PrintEdicts_f (void)
773 {
774         int             i;
775         const char *wildcard_fieldname;
776
777         if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
778         {
779                 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
780                 return;
781         }
782
783         PRVM_Begin;
784         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
785                 return;
786
787         if( Cmd_Argc() == 3)
788                 wildcard_fieldname = Cmd_Argv(2);
789         else
790                 wildcard_fieldname = NULL;
791
792         Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
793         for (i=0 ; i<prog->num_edicts ; i++)
794                 PRVM_ED_PrintNum (i, wildcard_fieldname);
795
796         PRVM_End;
797 }
798
799 /*
800 =============
801 PRVM_ED_PrintEdict_f
802
803 For debugging, prints a single edict
804 =============
805 */
806 void PRVM_ED_PrintEdict_f (void)
807 {
808         int             i;
809         const char      *wildcard_fieldname;
810
811         if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
812         {
813                 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
814                 return;
815         }
816
817         PRVM_Begin;
818         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
819                 return;
820
821         i = atoi (Cmd_Argv(2));
822         if (i >= prog->num_edicts)
823         {
824                 Con_Print("Bad edict number\n");
825                 PRVM_End;
826                 return;
827         }
828         if( Cmd_Argc() == 4)
829                 // Optional Wildcard Provided
830                 wildcard_fieldname = Cmd_Argv(3);
831         else
832                 // Use All
833                 wildcard_fieldname = NULL;
834         PRVM_ED_PrintNum (i, wildcard_fieldname);
835
836         PRVM_End;
837 }
838
839 /*
840 =============
841 PRVM_ED_Count
842
843 For debugging
844 =============
845 */
846 // 2 possibilities : 1. just displaying the active edict count
847 //                                       2. making a function pointer [x]
848 void PRVM_ED_Count_f (void)
849 {
850         int             i;
851         prvm_edict_t    *ent;
852         int             active;
853
854         if(Cmd_Argc() != 2)
855         {
856                 Con_Print("prvm_count <program name>\n");
857                 return;
858         }
859
860         PRVM_Begin;
861         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
862                 return;
863
864         if(prog->count_edicts)
865                 prog->count_edicts();
866         else
867         {
868                 active = 0;
869                 for (i=0 ; i<prog->num_edicts ; i++)
870                 {
871                         ent = PRVM_EDICT_NUM(i);
872                         if (ent->priv.required->free)
873                                 continue;
874                         active++;
875                 }
876
877                 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
878                 Con_Printf("active    :%3i\n", active);
879         }
880
881         PRVM_End;
882 }
883
884 /*
885 ==============================================================================
886
887                                         ARCHIVING GLOBALS
888
889 FIXME: need to tag constants, doesn't really work
890 ==============================================================================
891 */
892
893 /*
894 =============
895 PRVM_ED_WriteGlobals
896 =============
897 */
898 void PRVM_ED_WriteGlobals (qfile_t *f)
899 {
900         ddef_t          *def;
901         int                     i;
902         const char              *name;
903         int                     type;
904
905         FS_Print(f,"{\n");
906         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
907         {
908                 def = &prog->globaldefs[i];
909                 type = def->type;
910                 if ( !(def->type & DEF_SAVEGLOBAL) )
911                         continue;
912                 type &= ~DEF_SAVEGLOBAL;
913
914                 if (type != ev_string && type != ev_float && type != ev_entity)
915                         continue;
916
917                 name = PRVM_GetString(def->s_name);
918
919                 if(developer_entityparsing.integer)
920                         Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
921
922                 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
923                 FS_Printf(f,"\"%s\" ", name);
924                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
925                 prog->statestring = NULL;
926         }
927         FS_Print(f,"}\n");
928 }
929
930 /*
931 =============
932 PRVM_ED_ParseGlobals
933 =============
934 */
935 void PRVM_ED_ParseGlobals (const char *data)
936 {
937         char keyname[MAX_INPUTLINE];
938         ddef_t *key;
939
940         while (1)
941         {
942                 // parse key
943                 if (!COM_ParseToken_Simple(&data, false, false))
944                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
945                 if (com_token[0] == '}')
946                         break;
947
948                 if (developer_entityparsing.integer)
949                         Con_Printf("Key: \"%s\"", com_token);
950
951                 strlcpy (keyname, com_token, sizeof(keyname));
952
953                 // parse value
954                 if (!COM_ParseToken_Simple(&data, false, true))
955                         PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
956
957                 if (developer_entityparsing.integer)
958                         Con_Printf(" \"%s\"\n", com_token);
959
960                 if (com_token[0] == '}')
961                         PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
962
963                 key = PRVM_ED_FindGlobal (keyname);
964                 if (!key)
965                 {
966                         Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
967                         continue;
968                 }
969
970                 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
971                         PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
972         }
973 }
974
975 //============================================================================
976
977
978 /*
979 =============
980 PRVM_ED_ParseEval
981
982 Can parse either fields or globals
983 returns false if error
984 =============
985 */
986 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
987 {
988         int i, l;
989         char *new_p;
990         ddef_t *def;
991         prvm_eval_t *val;
992         mfunction_t *func;
993
994         if (ent)
995                 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
996         else
997                 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
998         switch (key->type & ~DEF_SAVEGLOBAL)
999         {
1000         case ev_string:
1001                 l = (int)strlen(s) + 1;
1002                 val->string = PRVM_AllocString(l, &new_p);
1003                 for (i = 0;i < l;i++)
1004                 {
1005                         if (s[i] == '\\' && s[i+1] && parsebackslash)
1006                         {
1007                                 i++;
1008                                 if (s[i] == 'n')
1009                                         *new_p++ = '\n';
1010                                 else if (s[i] == 'r')
1011                                         *new_p++ = '\r';
1012                                 else
1013                                         *new_p++ = s[i];
1014                         }
1015                         else
1016                                 *new_p++ = s[i];
1017                 }
1018                 break;
1019
1020         case ev_float:
1021                 while (*s && ISWHITESPACE(*s))
1022                         s++;
1023                 val->_float = atof(s);
1024                 break;
1025
1026         case ev_vector:
1027                 for (i = 0;i < 3;i++)
1028                 {
1029                         while (*s && ISWHITESPACE(*s))
1030                                 s++;
1031                         if (!*s)
1032                                 break;
1033                         val->vector[i] = atof(s);
1034                         while (!ISWHITESPACE(*s))
1035                                 s++;
1036                         if (!*s)
1037                                 break;
1038                 }
1039                 break;
1040
1041         case ev_entity:
1042                 while (*s && ISWHITESPACE(*s))
1043                         s++;
1044                 i = atoi(s);
1045                 if (i >= prog->limit_edicts)
1046                         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);
1047                 while (i >= prog->max_edicts)
1048                         PRVM_MEM_IncreaseEdicts();
1049                 // if IncreaseEdicts was called the base pointer needs to be updated
1050                 if (ent)
1051                         val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1052                 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1053                 break;
1054
1055         case ev_field:
1056                 if (*s != '.')
1057                 {
1058                         Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1059                         return false;
1060                 }
1061                 def = PRVM_ED_FindField(s + 1);
1062                 if (!def)
1063                 {
1064                         Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1065                         return false;
1066                 }
1067                 val->_int = def->ofs;
1068                 break;
1069
1070         case ev_function:
1071                 func = PRVM_ED_FindFunction(s);
1072                 if (!func)
1073                 {
1074                         Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1075                         return false;
1076                 }
1077                 val->function = func - prog->functions;
1078                 break;
1079
1080         default:
1081                 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1082                 return false;
1083         }
1084         return true;
1085 }
1086
1087 /*
1088 =============
1089 PRVM_GameCommand_f
1090
1091 Console command to send a string to QC function GameCommand of the
1092 indicated progs
1093
1094 Usage:
1095   sv_cmd adminmsg 3 "do not teamkill"
1096   cl_cmd someclientcommand
1097   menu_cmd somemenucommand
1098
1099 All progs can support this extension; sg calls it in server QC, cg in client
1100 QC, mg in menu QC.
1101 =============
1102 */
1103 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1104 {
1105         if(Cmd_Argc() < 1)
1106         {
1107                 Con_Printf("%s text...\n", whichcmd);
1108                 return;
1109         }
1110
1111         PRVM_Begin;
1112         if(!PRVM_SetProgFromString(whichprogs))
1113         // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1114         // also, it makes printing error messages easier!
1115         {
1116                 Con_Printf("%s program not loaded.\n", whichprogs);
1117                 return;
1118         }
1119
1120         if(!prog->funcoffsets.GameCommand)
1121         {
1122                 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1123         }
1124         else
1125         {
1126                 int restorevm_tempstringsbuf_cursize;
1127                 const char *s;
1128
1129                 s = Cmd_Args();
1130
1131                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1132                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1133                 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1134                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1135         }
1136
1137         PRVM_End;
1138 }
1139 void PRVM_GameCommand_Server_f(void)
1140 {
1141         PRVM_GameCommand("server", "sv_cmd");
1142 }
1143 void PRVM_GameCommand_Client_f(void)
1144 {
1145         PRVM_GameCommand("client", "cl_cmd");
1146 }
1147 void PRVM_GameCommand_Menu_f(void)
1148 {
1149         PRVM_GameCommand("menu", "menu_cmd");
1150 }
1151
1152 /*
1153 =============
1154 PRVM_ED_EdictGet_f
1155
1156 Console command to load a field of a specified edict
1157 =============
1158 */
1159 void PRVM_ED_EdictGet_f(void)
1160 {
1161         prvm_edict_t *ed;
1162         ddef_t *key;
1163         const char *s;
1164         prvm_eval_t *v;
1165
1166         if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1167         {
1168                 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1169                 return;
1170         }
1171
1172         PRVM_Begin;
1173         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1174         {
1175                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1176                 return;
1177         }
1178
1179         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1180
1181         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1182         {
1183                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1184                 goto fail;
1185         }
1186
1187         v = (prvm_eval_t *)((char *)ed->fields.vp + key->ofs*4);
1188         s = PRVM_UglyValueString(key->type, v);
1189         if(Cmd_Argc() == 5)
1190         {
1191                 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1192                 if (cvar && cvar->flags & CVAR_READONLY)
1193                 {
1194                         Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1195                         goto fail;
1196                 }
1197                 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1198         }
1199         else
1200                 Con_Printf("%s\n", s);
1201
1202 fail:
1203         PRVM_End;
1204 }
1205
1206 void PRVM_ED_GlobalGet_f(void)
1207 {
1208         ddef_t *key;
1209         const char *s;
1210         prvm_eval_t *v;
1211
1212         if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1213         {
1214                 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1215                 return;
1216         }
1217
1218         PRVM_Begin;
1219         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1220         {
1221                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1222                 return;
1223         }
1224
1225         key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1226         if(!key)
1227         {
1228                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1229                 goto fail;
1230         }
1231
1232         v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1233         s = PRVM_UglyValueString(key->type, v);
1234         if(Cmd_Argc() == 4)
1235         {
1236                 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1237                 if (cvar && cvar->flags & CVAR_READONLY)
1238                 {
1239                         Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1240                         goto fail;
1241                 }
1242                 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1243         }
1244         else
1245                 Con_Printf("%s\n", s);
1246
1247 fail:
1248         PRVM_End;
1249 }
1250
1251 /*
1252 =============
1253 PRVM_ED_EdictSet_f
1254
1255 Console command to set a field of a specified edict
1256 =============
1257 */
1258 void PRVM_ED_EdictSet_f(void)
1259 {
1260         prvm_edict_t *ed;
1261         ddef_t *key;
1262
1263         if(Cmd_Argc() != 5)
1264         {
1265                 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1266                 return;
1267         }
1268
1269         PRVM_Begin;
1270         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1271         {
1272                 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1273                 return;
1274         }
1275
1276         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1277
1278         if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1279                 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1280         else
1281                 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1282
1283         PRVM_End;
1284 }
1285
1286 /*
1287 ====================
1288 PRVM_ED_ParseEdict
1289
1290 Parses an edict out of the given string, returning the new position
1291 ed should be a properly initialized empty edict.
1292 Used for initial level load and for savegames.
1293 ====================
1294 */
1295 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1296 {
1297         ddef_t *key;
1298         qboolean anglehack;
1299         qboolean init;
1300         char keyname[256];
1301         size_t n;
1302
1303         init = false;
1304
1305 // go through all the dictionary pairs
1306         while (1)
1307         {
1308         // parse key
1309                 if (!COM_ParseToken_Simple(&data, false, false))
1310                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1311                 if (developer_entityparsing.integer)
1312                         Con_Printf("Key: \"%s\"", com_token);
1313                 if (com_token[0] == '}')
1314                         break;
1315
1316                 // anglehack is to allow QuakeEd to write single scalar angles
1317                 // and allow them to be turned into vectors. (FIXME...)
1318                 if (!strcmp(com_token, "angle"))
1319                 {
1320                         strlcpy (com_token, "angles", sizeof(com_token));
1321                         anglehack = true;
1322                 }
1323                 else
1324                         anglehack = false;
1325
1326                 // FIXME: change light to _light to get rid of this hack
1327                 if (!strcmp(com_token, "light"))
1328                         strlcpy (com_token, "light_lev", sizeof(com_token));    // hack for single light def
1329
1330                 strlcpy (keyname, com_token, sizeof(keyname));
1331
1332                 // another hack to fix keynames with trailing spaces
1333                 n = strlen(keyname);
1334                 while (n && keyname[n-1] == ' ')
1335                 {
1336                         keyname[n-1] = 0;
1337                         n--;
1338                 }
1339
1340         // parse value
1341                 if (!COM_ParseToken_Simple(&data, false, false))
1342                         PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1343                 if (developer_entityparsing.integer)
1344                         Con_Printf(" \"%s\"\n", com_token);
1345
1346                 if (com_token[0] == '}')
1347                         PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1348
1349                 init = true;
1350
1351                 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1352                 if (!keyname[0])
1353                         continue;
1354
1355 // keynames with a leading underscore are used for utility comments,
1356 // and are immediately discarded by quake
1357                 if (keyname[0] == '_')
1358                         continue;
1359
1360                 key = PRVM_ED_FindField (keyname);
1361                 if (!key)
1362                 {
1363                         Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1364                         continue;
1365                 }
1366
1367                 if (anglehack)
1368                 {
1369                         char    temp[32];
1370                         strlcpy (temp, com_token, sizeof(temp));
1371                         dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1372                 }
1373
1374                 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1375                         PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1376         }
1377
1378         if (!init)
1379                 ent->priv.required->free = true;
1380
1381         return data;
1382 }
1383
1384
1385 /*
1386 ================
1387 PRVM_ED_LoadFromFile
1388
1389 The entities are directly placed in the array, rather than allocated with
1390 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1391 number references out of order.
1392
1393 Creates a server's entity / program execution context by
1394 parsing textual entity definitions out of an ent file.
1395
1396 Used for both fresh maps and savegame loads.  A fresh map would also need
1397 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1398 ================
1399 */
1400 void PRVM_ED_LoadFromFile (const char *data)
1401 {
1402         prvm_edict_t *ent;
1403         int parsed, inhibited, spawned, died;
1404         const char *funcname;
1405         mfunction_t *func;
1406
1407         parsed = 0;
1408         inhibited = 0;
1409         spawned = 0;
1410         died = 0;
1411
1412
1413 // parse ents
1414         while (1)
1415         {
1416 // parse the opening brace
1417                 if (!COM_ParseToken_Simple(&data, false, false))
1418                         break;
1419                 if (com_token[0] != '{')
1420                         PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1421
1422                 // CHANGED: this is not conform to PR_LoadFromFile
1423                 if(prog->loadintoworld)
1424                 {
1425                         prog->loadintoworld = false;
1426                         ent = PRVM_EDICT_NUM(0);
1427                 }
1428                 else
1429                         ent = PRVM_ED_Alloc();
1430
1431                 // clear it
1432                 if (ent != prog->edicts)        // hack
1433                         memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1434
1435                 data = PRVM_ED_ParseEdict (data, ent);
1436                 parsed++;
1437
1438                 // remove the entity ?
1439                 if(prog->load_edict && !prog->load_edict(ent))
1440                 {
1441                         PRVM_ED_Free(ent);
1442                         inhibited++;
1443                         continue;
1444                 }
1445
1446                 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1447                 {
1448                         // self = ent
1449                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1450                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1451                 }
1452
1453                 if(ent->priv.required->free)
1454                 {
1455                         inhibited++;
1456                         continue;
1457                 }
1458
1459 //
1460 // immediately call spawn function, but only if there is a self global and a classname
1461 //
1462                 if(!ent->priv.required->free)
1463                 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1464                 {
1465                         string_t handle =  PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1466                         if (!handle)
1467                         {
1468                                 Con_Print("No classname for:\n");
1469                                 PRVM_ED_Print(ent, NULL);
1470                                 PRVM_ED_Free (ent);
1471                                 continue;
1472                         }
1473
1474                         // look for the spawn function
1475                         funcname = PRVM_GetString(handle);
1476                         func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1477                         if(!func)
1478                                 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1479                                         func = PRVM_ED_FindFunction (funcname);
1480
1481                         if (!func)
1482                         {
1483                                 // check for OnEntityNoSpawnFunction
1484                                 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1485                                 {
1486                                         // self = ent
1487                                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1488                                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1489                                 }
1490                                 else
1491                                 {
1492                                         if (developer.integer) // don't confuse non-developers with errors
1493                                         {
1494                                                 Con_Print("No spawn function for:\n");
1495                                                 PRVM_ED_Print(ent, NULL);
1496                                         }
1497                                         PRVM_ED_Free (ent);
1498                                         continue; // not included in "inhibited" count
1499                                 }
1500                         }
1501                         else
1502                         {
1503                                 // self = ent
1504                                 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1505                                 PRVM_ExecuteProgram (func - prog->functions, "");
1506                         }
1507                 }
1508
1509                 if(!ent->priv.required->free)
1510                 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1511                 {
1512                         // self = ent
1513                         PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1514                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1515                 }
1516
1517                 spawned++;
1518                 if (ent->priv.required->free)
1519                         died++;
1520         }
1521
1522         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);
1523 }
1524
1525 void PRVM_FindOffsets(void)
1526 {
1527         // field and global searches use -1 for NULL
1528         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1529         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1530         // functions use 0 for NULL
1531         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1532
1533         // server and client qc use a lot of similar fields, so this is combined
1534         prog->fieldoffsets.SendEntity                     = PRVM_ED_FindFieldOffset("SendEntity");
1535         prog->fieldoffsets.SendFlags                      = PRVM_ED_FindFieldOffset("SendFlags");
1536         prog->fieldoffsets.Version                        = PRVM_ED_FindFieldOffset("Version");
1537         prog->fieldoffsets.alpha                          = PRVM_ED_FindFieldOffset("alpha");
1538         prog->fieldoffsets.ammo_cells1                    = PRVM_ED_FindFieldOffset("ammo_cells1");
1539         prog->fieldoffsets.ammo_lava_nails                = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1540         prog->fieldoffsets.ammo_multi_rockets             = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1541         prog->fieldoffsets.ammo_nails1                    = PRVM_ED_FindFieldOffset("ammo_nails1");
1542         prog->fieldoffsets.ammo_plasma                    = PRVM_ED_FindFieldOffset("ammo_plasma");
1543         prog->fieldoffsets.ammo_rockets1                  = PRVM_ED_FindFieldOffset("ammo_rockets1");
1544         prog->fieldoffsets.ammo_shells1                   = PRVM_ED_FindFieldOffset("ammo_shells1");
1545         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1546         prog->fieldoffsets.button3                        = PRVM_ED_FindFieldOffset("button3");
1547         prog->fieldoffsets.button4                        = PRVM_ED_FindFieldOffset("button4");
1548         prog->fieldoffsets.button5                        = PRVM_ED_FindFieldOffset("button5");
1549         prog->fieldoffsets.button6                        = PRVM_ED_FindFieldOffset("button6");
1550         prog->fieldoffsets.button7                        = PRVM_ED_FindFieldOffset("button7");
1551         prog->fieldoffsets.button8                        = PRVM_ED_FindFieldOffset("button8");
1552         prog->fieldoffsets.button9                        = PRVM_ED_FindFieldOffset("button9");
1553         prog->fieldoffsets.button10                       = PRVM_ED_FindFieldOffset("button10");
1554         prog->fieldoffsets.button11                       = PRVM_ED_FindFieldOffset("button11");
1555         prog->fieldoffsets.button12                       = PRVM_ED_FindFieldOffset("button12");
1556         prog->fieldoffsets.button13                       = PRVM_ED_FindFieldOffset("button13");
1557         prog->fieldoffsets.button14                       = PRVM_ED_FindFieldOffset("button14");
1558         prog->fieldoffsets.button15                       = PRVM_ED_FindFieldOffset("button15");
1559         prog->fieldoffsets.button16                       = PRVM_ED_FindFieldOffset("button16");
1560         prog->fieldoffsets.buttonchat                     = PRVM_ED_FindFieldOffset("buttonchat");
1561         prog->fieldoffsets.buttonuse                      = PRVM_ED_FindFieldOffset("buttonuse");
1562         prog->fieldoffsets.chain                          = PRVM_ED_FindFieldOffset("chain");
1563         prog->fieldoffsets.classname                      = PRVM_ED_FindFieldOffset("classname");
1564         prog->fieldoffsets.clientcamera                   = PRVM_ED_FindFieldOffset("clientcamera");
1565         prog->fieldoffsets.clientcolors                   = PRVM_ED_FindFieldOffset("clientcolors");
1566         prog->fieldoffsets.clientstatus                   = PRVM_ED_FindFieldOffset("clientstatus");
1567         prog->fieldoffsets.color                          = PRVM_ED_FindFieldOffset("color");
1568         prog->fieldoffsets.colormod                       = PRVM_ED_FindFieldOffset("colormod");
1569         prog->fieldoffsets.contentstransition             = PRVM_ED_FindFieldOffset("contentstransition");
1570         prog->fieldoffsets.cursor_active                  = PRVM_ED_FindFieldOffset("cursor_active");
1571         prog->fieldoffsets.cursor_screen                  = PRVM_ED_FindFieldOffset("cursor_screen");
1572         prog->fieldoffsets.cursor_trace_endpos            = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1573         prog->fieldoffsets.cursor_trace_ent               = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1574         prog->fieldoffsets.cursor_trace_start             = PRVM_ED_FindFieldOffset("cursor_trace_start");
1575         prog->fieldoffsets.customizeentityforclient       = PRVM_ED_FindFieldOffset("customizeentityforclient");
1576         prog->fieldoffsets.dimension_hit                  = PRVM_ED_FindFieldOffset("dimension_hit");
1577         prog->fieldoffsets.dimension_solid                = PRVM_ED_FindFieldOffset("dimension_solid");
1578         prog->fieldoffsets.disableclientprediction        = PRVM_ED_FindFieldOffset("disableclientprediction");
1579         prog->fieldoffsets.dphitcontentsmask              = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1580         prog->fieldoffsets.drawonlytoclient               = PRVM_ED_FindFieldOffset("drawonlytoclient");
1581         prog->fieldoffsets.exteriormodeltoclient          = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1582         prog->fieldoffsets.fatness                        = PRVM_ED_FindFieldOffset("fatness");
1583         prog->fieldoffsets.forceshader                    = PRVM_ED_FindFieldOffset("forceshader");
1584         prog->fieldoffsets.frame                          = PRVM_ED_FindFieldOffset("frame");
1585         prog->fieldoffsets.frame1time                     = PRVM_ED_FindFieldOffset("frame1time");
1586         prog->fieldoffsets.frame2                         = PRVM_ED_FindFieldOffset("frame2");
1587         prog->fieldoffsets.frame2time                     = PRVM_ED_FindFieldOffset("frame2time");
1588         prog->fieldoffsets.frame3                         = PRVM_ED_FindFieldOffset("frame3");
1589         prog->fieldoffsets.frame3time                     = PRVM_ED_FindFieldOffset("frame3time");
1590         prog->fieldoffsets.frame4                         = PRVM_ED_FindFieldOffset("frame4");
1591         prog->fieldoffsets.frame4time                     = PRVM_ED_FindFieldOffset("frame4time");
1592         prog->fieldoffsets.fullbright                     = PRVM_ED_FindFieldOffset("fullbright");
1593         prog->fieldoffsets.glow_color                     = PRVM_ED_FindFieldOffset("glow_color");
1594         prog->fieldoffsets.glow_size                      = PRVM_ED_FindFieldOffset("glow_size");
1595         prog->fieldoffsets.glow_trail                     = PRVM_ED_FindFieldOffset("glow_trail");
1596         prog->fieldoffsets.gravity                        = PRVM_ED_FindFieldOffset("gravity");
1597         prog->fieldoffsets.groundentity                   = PRVM_ED_FindFieldOffset("groundentity");
1598         prog->fieldoffsets.hull                           = PRVM_ED_FindFieldOffset("hull");
1599         prog->fieldoffsets.ideal_yaw                      = PRVM_ED_FindFieldOffset("ideal_yaw");
1600         prog->fieldoffsets.idealpitch                     = PRVM_ED_FindFieldOffset("idealpitch");
1601         prog->fieldoffsets.items2                         = PRVM_ED_FindFieldOffset("items2");
1602         prog->fieldoffsets.lerpfrac                       = PRVM_ED_FindFieldOffset("lerpfrac");
1603         prog->fieldoffsets.lerpfrac3                      = PRVM_ED_FindFieldOffset("lerpfrac3");
1604         prog->fieldoffsets.lerpfrac4                      = PRVM_ED_FindFieldOffset("lerpfrac4");
1605         prog->fieldoffsets.light_lev                      = PRVM_ED_FindFieldOffset("light_lev");
1606         prog->fieldoffsets.message                        = PRVM_ED_FindFieldOffset("message");
1607         prog->fieldoffsets.modelflags                     = PRVM_ED_FindFieldOffset("modelflags");
1608         prog->fieldoffsets.movement                       = PRVM_ED_FindFieldOffset("movement");
1609         prog->fieldoffsets.movetypesteplandevent          = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1610         prog->fieldoffsets.netaddress                     = PRVM_ED_FindFieldOffset("netaddress");
1611         prog->fieldoffsets.nextthink                      = PRVM_ED_FindFieldOffset("nextthink");
1612         prog->fieldoffsets.nodrawtoclient                 = PRVM_ED_FindFieldOffset("nodrawtoclient");
1613         prog->fieldoffsets.pflags                         = PRVM_ED_FindFieldOffset("pflags");
1614         prog->fieldoffsets.ping                           = PRVM_ED_FindFieldOffset("ping");
1615         prog->fieldoffsets.pitch_speed                    = PRVM_ED_FindFieldOffset("pitch_speed");
1616         prog->fieldoffsets.playermodel                    = PRVM_ED_FindFieldOffset("playermodel");
1617         prog->fieldoffsets.playerskin                     = PRVM_ED_FindFieldOffset("playerskin");
1618         prog->fieldoffsets.pmodel                         = PRVM_ED_FindFieldOffset("pmodel");
1619         prog->fieldoffsets.punchvector                    = PRVM_ED_FindFieldOffset("punchvector");
1620         prog->fieldoffsets.renderamt                      = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1621         prog->fieldoffsets.renderflags                    = PRVM_ED_FindFieldOffset("renderflags");
1622         prog->fieldoffsets.rendermode                     = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1623         prog->fieldoffsets.scale                          = PRVM_ED_FindFieldOffset("scale");
1624         prog->fieldoffsets.shadertime                     = PRVM_ED_FindFieldOffset("shadertime");
1625         prog->fieldoffsets.style                          = PRVM_ED_FindFieldOffset("style");
1626         prog->fieldoffsets.tag_entity                     = PRVM_ED_FindFieldOffset("tag_entity");
1627         prog->fieldoffsets.tag_index                      = PRVM_ED_FindFieldOffset("tag_index");
1628         prog->fieldoffsets.think                          = PRVM_ED_FindFieldOffset("think");
1629         prog->fieldoffsets.viewmodelforclient             = PRVM_ED_FindFieldOffset("viewmodelforclient");
1630         prog->fieldoffsets.viewzoom                       = PRVM_ED_FindFieldOffset("viewzoom");
1631         prog->fieldoffsets.yaw_speed                      = PRVM_ED_FindFieldOffset("yaw_speed");
1632         prog->fieldoffsets.bouncefactor                   = PRVM_ED_FindFieldOffset("bouncefactor");
1633         prog->fieldoffsets.bouncestop                     = PRVM_ED_FindFieldOffset("bouncestop");
1634         prog->funcoffsets.CSQC_ConsoleCommand             = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1635         prog->funcoffsets.CSQC_Ent_Remove                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1636         prog->funcoffsets.CSQC_Ent_Spawn                  = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1637         prog->funcoffsets.CSQC_Ent_Update                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1638         prog->funcoffsets.CSQC_Event                      = PRVM_ED_FindFunctionOffset("CSQC_Event");
1639         prog->funcoffsets.CSQC_Event_Sound                = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1640         prog->funcoffsets.CSQC_Init                       = PRVM_ED_FindFunctionOffset("CSQC_Init");
1641         prog->funcoffsets.CSQC_InputEvent                 = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1642         prog->funcoffsets.CSQC_Parse_CenterPrint          = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1643         prog->funcoffsets.CSQC_Parse_Print                = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1644         prog->funcoffsets.CSQC_Parse_StuffCmd             = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1645         prog->funcoffsets.CSQC_Parse_TempEntity           = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1646         prog->funcoffsets.CSQC_Shutdown                   = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1647         prog->funcoffsets.CSQC_UpdateView                 = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1648         prog->funcoffsets.EndFrame                        = PRVM_ED_FindFunctionOffset("EndFrame");
1649         prog->funcoffsets.GameCommand                     = PRVM_ED_FindFunctionOffset("GameCommand");
1650         prog->funcoffsets.Gecko_Query                     = PRVM_ED_FindFunctionOffset("Gecko_Query");
1651         prog->funcoffsets.RestoreGame                     = PRVM_ED_FindFunctionOffset("RestoreGame");
1652         prog->funcoffsets.SV_ChangeTeam                   = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1653         prog->funcoffsets.SV_OnEntityNoSpawnFunction      = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1654         prog->funcoffsets.SV_OnEntityPostSpawnFunction    = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1655         prog->funcoffsets.SV_OnEntityPreSpawnFunction     = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1656         prog->funcoffsets.SV_ParseClientCommand           = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1657         prog->funcoffsets.SV_PausedTic                    = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1658         prog->funcoffsets.SV_PlayerPhysics                = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1659         prog->funcoffsets.SV_Shutdown                     = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1660         prog->funcoffsets.URI_Get_Callback                = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1661         prog->globaloffsets.SV_InitCmd                    = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1662         prog->globaloffsets.coop                          = PRVM_ED_FindGlobalOffset("coop");
1663         prog->globaloffsets.deathmatch                    = PRVM_ED_FindGlobalOffset("deathmatch");
1664         prog->globaloffsets.dmg_origin                    = PRVM_ED_FindGlobalOffset("dmg_origin");
1665         prog->globaloffsets.dmg_save                      = PRVM_ED_FindGlobalOffset("dmg_save");
1666         prog->globaloffsets.dmg_take                      = PRVM_ED_FindGlobalOffset("dmg_take");
1667         prog->globaloffsets.drawfont                      = PRVM_ED_FindGlobalOffset("drawfont");
1668         prog->globaloffsets.gettaginfo_forward            = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1669         prog->globaloffsets.gettaginfo_name               = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1670         prog->globaloffsets.gettaginfo_offset             = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1671         prog->globaloffsets.gettaginfo_parent             = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1672         prog->globaloffsets.gettaginfo_right              = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1673         prog->globaloffsets.gettaginfo_up                 = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1674         prog->globaloffsets.intermission                  = PRVM_ED_FindGlobalOffset("intermission");
1675         prog->globaloffsets.require_spawnfunc_prefix      = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1676         prog->globaloffsets.sb_showscores                 = PRVM_ED_FindGlobalOffset("sb_showscores");
1677         prog->globaloffsets.self                          = PRVM_ED_FindGlobalOffset("self");
1678         prog->globaloffsets.serverdeltatime               = PRVM_ED_FindGlobalOffset("serverdeltatime");
1679         prog->globaloffsets.serverprevtime                = PRVM_ED_FindGlobalOffset("serverprevtime");
1680         prog->globaloffsets.servertime                    = PRVM_ED_FindGlobalOffset("servertime");
1681         prog->globaloffsets.time                          = PRVM_ED_FindGlobalOffset("time");
1682         prog->globaloffsets.trace_allsolid                = PRVM_ED_FindGlobalOffset("trace_allsolid");
1683         prog->globaloffsets.trace_dphitcontents           = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1684         prog->globaloffsets.trace_dphitq3surfaceflags     = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1685         prog->globaloffsets.trace_dphittexturename        = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1686         prog->globaloffsets.trace_dpstartcontents         = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1687         prog->globaloffsets.trace_endpos                  = PRVM_ED_FindGlobalOffset("trace_endpos");
1688         prog->globaloffsets.trace_ent                     = PRVM_ED_FindGlobalOffset("trace_ent");
1689         prog->globaloffsets.trace_fraction                = PRVM_ED_FindGlobalOffset("trace_fraction");
1690         prog->globaloffsets.trace_inopen                  = PRVM_ED_FindGlobalOffset("trace_inopen");
1691         prog->globaloffsets.trace_inwater                 = PRVM_ED_FindGlobalOffset("trace_inwater");
1692         prog->globaloffsets.trace_networkentity           = PRVM_ED_FindGlobalOffset("trace_networkentity");
1693         prog->globaloffsets.trace_plane_dist              = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1694         prog->globaloffsets.trace_plane_normal            = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1695         prog->globaloffsets.trace_startsolid              = PRVM_ED_FindGlobalOffset("trace_startsolid");
1696         prog->globaloffsets.v_forward                     = PRVM_ED_FindGlobalOffset("v_forward");
1697         prog->globaloffsets.v_right                       = PRVM_ED_FindGlobalOffset("v_right");
1698         prog->globaloffsets.v_up                          = PRVM_ED_FindGlobalOffset("v_up");
1699         prog->globaloffsets.view_angles                   = PRVM_ED_FindGlobalOffset("view_angles");
1700         prog->globaloffsets.worldstatus                   = PRVM_ED_FindGlobalOffset("worldstatus");
1701
1702         // menu qc only uses some functions, nothing else
1703         prog->funcoffsets.m_draw                          = PRVM_ED_FindFunctionOffset("m_draw");
1704         prog->funcoffsets.m_init                          = PRVM_ED_FindFunctionOffset("m_init");
1705         prog->funcoffsets.m_keydown                       = PRVM_ED_FindFunctionOffset("m_keydown");
1706         prog->funcoffsets.m_keyup                         = PRVM_ED_FindFunctionOffset("m_keyup");
1707         prog->funcoffsets.m_shutdown                      = PRVM_ED_FindFunctionOffset("m_shutdown");
1708         prog->funcoffsets.m_toggle                        = PRVM_ED_FindFunctionOffset("m_toggle");
1709 }
1710
1711 // not used
1712 /*
1713 typedef struct dpfield_s
1714 {
1715         int type;
1716         char *string;
1717 }
1718 dpfield_t;
1719
1720 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1721
1722 dpfield_t dpfields[] =
1723 {
1724 };
1725 */
1726
1727 /*
1728 ===============
1729 PRVM_ResetProg
1730 ===============
1731 */
1732
1733 void PRVM_LeakTest(void);
1734 void PRVM_ResetProg(void)
1735 {
1736         PRVM_LeakTest();
1737         PRVM_GCALL(reset_cmd)();
1738         Mem_FreePool(&prog->progs_mempool);
1739         memset(prog,0,sizeof(prvm_prog_t));
1740         prog->starttime = Sys_DoubleTime();
1741 }
1742
1743 /*
1744 ===============
1745 PRVM_LoadLNO
1746 ===============
1747 */
1748 void PRVM_LoadLNO( const char *progname ) {
1749         fs_offset_t filesize;
1750         unsigned char *lno;
1751         unsigned int *header;
1752         char filename[512];
1753
1754         FS_StripExtension( progname, filename, sizeof( filename ) );
1755         strlcat( filename, ".lno", sizeof( filename ) );
1756
1757         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1758         if( !lno ) {
1759                 return;
1760         }
1761
1762 /*
1763 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
1764 <Spike>    SafeWrite (h, &version, sizeof(int));
1765 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
1766 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
1767 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
1768 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
1769 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1770 */
1771         if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1772                 Mem_Free(lno);
1773                 return;
1774         }
1775
1776         header = (unsigned int *) lno;
1777         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1778                 LittleLong( header[ 1 ] ) == 1 &&
1779                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1780                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1781                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1782                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1783         {
1784                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1785                 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1786         }
1787         Mem_Free( lno );
1788 }
1789
1790 /*
1791 ===============
1792 PRVM_LoadProgs
1793 ===============
1794 */
1795 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1796 {
1797         int i;
1798         dstatement_t *st;
1799         ddef_t *infielddefs;
1800         dfunction_t *dfunctions;
1801         fs_offset_t filesize;
1802
1803         if( prog->loaded ) {
1804                 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1805         }
1806
1807         prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1808         if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1809                 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1810         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1811
1812         Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1813
1814         prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1815
1816 // byte swap the header
1817         for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1818                 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1819
1820         if (prog->progs->version != PROG_VERSION)
1821                 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1822         if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1823                 PRVM_ERROR ("%s: %s system vars have been modified (CRC of progs.dat systemvars %i != engine %i), progdefs.h is out of date", PRVM_NAME, filename, prog->progs->crc, prog->headercrc);
1824
1825         //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1826         dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1827
1828         if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1829                 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1830         prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1831         prog->stringssize = prog->progs->numstrings;
1832
1833         prog->numknownstrings = 0;
1834         prog->maxknownstrings = 0;
1835         prog->knownstrings = NULL;
1836         prog->knownstrings_freeable = NULL;
1837
1838         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1839
1840         prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1841
1842         // we need to expand the fielddefs list to include all the engine fields,
1843         // so allocate a new place for it
1844         infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1845         //                                                                                              ( + DPFIELDS                       )
1846         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1847
1848         prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1849
1850         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1851
1852         // moved edict_size calculation down below field adding code
1853
1854         //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1855         prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1856
1857 // byte swap the lumps
1858         for (i=0 ; i<prog->progs->numstatements ; i++)
1859         {
1860                 prog->statements[i].op = LittleShort(prog->statements[i].op);
1861                 prog->statements[i].a = LittleShort(prog->statements[i].a);
1862                 prog->statements[i].b = LittleShort(prog->statements[i].b);
1863                 prog->statements[i].c = LittleShort(prog->statements[i].c);
1864         }
1865
1866         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1867         for (i = 0;i < prog->progs->numfunctions;i++)
1868         {
1869                 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1870                 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1871                 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1872                 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1873                 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1874                 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1875                 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1876                 if(prog->functions[i].first_statement >= prog->progs->numstatements)
1877                         PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
1878                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1879         }
1880
1881         for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1882         {
1883                 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1884                 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1885                 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1886                 // TODO bounds check ofs, s_name
1887         }
1888
1889         // copy the progs fields to the new fields list
1890         for (i = 0;i < prog->progs->numfielddefs;i++)
1891         {
1892                 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1893                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1894                         PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1895                 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1896                 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1897                 // TODO bounds check ofs, s_name
1898         }
1899
1900         // append the required fields
1901         for (i = 0;i < (int) numrequiredfields;i++)
1902         {
1903                 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1904                 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1905                 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1906                 // TODO bounds check ofs, s_name
1907                 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1908                         prog->progs->entityfields += 3;
1909                 else
1910                         prog->progs->entityfields++;
1911                 prog->progs->numfielddefs++;
1912         }
1913
1914         // check required functions
1915         for(i=0 ; i < numrequiredfunc ; i++)
1916                 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1917                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1918
1919         // check required globals
1920         for(i=0 ; i < numrequiredglobals ; i++)
1921                 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1922                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1923
1924         for (i=0 ; i<prog->progs->numglobals ; i++)
1925                 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1926
1927         // moved edict_size calculation down here, below field adding code
1928         // LordHavoc: this no longer includes the prvm_edict_t header
1929         prog->edict_size = prog->progs->entityfields * 4;
1930         prog->edictareasize = prog->edict_size * prog->limit_edicts;
1931
1932         // LordHavoc: bounds check anything static
1933         for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1934         {
1935                 switch (st->op)
1936                 {
1937                 case OP_IF:
1938                 case OP_IFNOT:
1939                         if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1940                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1941                         break;
1942                 case OP_GOTO:
1943                         if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1944                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1945                         break;
1946                 // global global global
1947                 case OP_ADD_F:
1948                 case OP_ADD_V:
1949                 case OP_SUB_F:
1950                 case OP_SUB_V:
1951                 case OP_MUL_F:
1952                 case OP_MUL_V:
1953                 case OP_MUL_FV:
1954                 case OP_MUL_VF:
1955                 case OP_DIV_F:
1956                 case OP_BITAND:
1957                 case OP_BITOR:
1958                 case OP_GE:
1959                 case OP_LE:
1960                 case OP_GT:
1961                 case OP_LT:
1962                 case OP_AND:
1963                 case OP_OR:
1964                 case OP_EQ_F:
1965                 case OP_EQ_V:
1966                 case OP_EQ_S:
1967                 case OP_EQ_E:
1968                 case OP_EQ_FNC:
1969                 case OP_NE_F:
1970                 case OP_NE_V:
1971                 case OP_NE_S:
1972                 case OP_NE_E:
1973                 case OP_NE_FNC:
1974                 case OP_ADDRESS:
1975                 case OP_LOAD_F:
1976                 case OP_LOAD_FLD:
1977                 case OP_LOAD_ENT:
1978                 case OP_LOAD_S:
1979                 case OP_LOAD_FNC:
1980                 case OP_LOAD_V:
1981                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1982                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1983                         break;
1984                 // global none global
1985                 case OP_NOT_F:
1986                 case OP_NOT_V:
1987                 case OP_NOT_S:
1988                 case OP_NOT_FNC:
1989                 case OP_NOT_ENT:
1990                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1991                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1992                         break;
1993                 // 2 globals
1994                 case OP_STOREP_F:
1995                 case OP_STOREP_ENT:
1996                 case OP_STOREP_FLD:
1997                 case OP_STOREP_S:
1998                 case OP_STOREP_FNC:
1999                 case OP_STORE_F:
2000                 case OP_STORE_ENT:
2001                 case OP_STORE_FLD:
2002                 case OP_STORE_S:
2003                 case OP_STORE_FNC:
2004                 case OP_STATE:
2005                 case OP_STOREP_V:
2006                 case OP_STORE_V:
2007                         if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2008                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2009                         break;
2010                 // 1 global
2011                 case OP_CALL0:
2012                 case OP_CALL1:
2013                 case OP_CALL2:
2014                 case OP_CALL3:
2015                 case OP_CALL4:
2016                 case OP_CALL5:
2017                 case OP_CALL6:
2018                 case OP_CALL7:
2019                 case OP_CALL8:
2020                 case OP_DONE:
2021                 case OP_RETURN:
2022                         if ((unsigned short) st->a >= prog->progs->numglobals)
2023                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2024                         break;
2025                 default:
2026                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2027                         break;
2028                 }
2029         }
2030         if(prog->progs->numstatements < 1)
2031         {
2032                 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2033         }
2034         else switch(prog->statements[prog->progs->numstatements - 1].op)
2035         {
2036                 case OP_RETURN:
2037                 case OP_GOTO:
2038                 case OP_DONE:
2039                         break;
2040                 default:
2041                         PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2042                         break;
2043         }
2044
2045         PRVM_LoadLNO(filename);
2046
2047         PRVM_Init_Exec();
2048
2049         prog->loaded = TRUE;
2050
2051         // set flags & ddef_ts in prog
2052
2053         prog->flag = 0;
2054
2055         PRVM_FindOffsets();
2056
2057         PRVM_GCALL(init_cmd)();
2058
2059         // init mempools
2060         PRVM_MEM_Alloc();
2061 }
2062
2063
2064 void PRVM_Fields_f (void)
2065 {
2066         int i, j, ednum, used, usedamount;
2067         int *counts;
2068         char tempstring[MAX_INPUTLINE], tempstring2[260];
2069         const char *name;
2070         prvm_edict_t *ed;
2071         ddef_t *d;
2072         int *v;
2073
2074         // TODO
2075         /*
2076         if (!sv.active)
2077         {
2078                 Con_Print("no progs loaded\n");
2079                 return;
2080         }
2081         */
2082
2083         if(Cmd_Argc() != 2)
2084         {
2085                 Con_Print("prvm_fields <program name>\n");
2086                 return;
2087         }
2088
2089         PRVM_Begin;
2090         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2091                 return;
2092
2093         counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2094         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2095         {
2096                 ed = PRVM_EDICT_NUM(ednum);
2097                 if (ed->priv.required->free)
2098                         continue;
2099                 for (i = 1;i < prog->progs->numfielddefs;i++)
2100                 {
2101                         d = &prog->fielddefs[i];
2102                         name = PRVM_GetString(d->s_name);
2103                         if (name[strlen(name)-2] == '_')
2104                                 continue;       // skip _x, _y, _z vars
2105                         v = (int *)((char *)ed->fields.vp + d->ofs*4);
2106                         // if the value is still all 0, skip the field
2107                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2108                         {
2109                                 if (v[j])
2110                                 {
2111                                         counts[i]++;
2112                                         break;
2113                                 }
2114                         }
2115                 }
2116         }
2117         used = 0;
2118         usedamount = 0;
2119         tempstring[0] = 0;
2120         for (i = 0;i < prog->progs->numfielddefs;i++)
2121         {
2122                 d = &prog->fielddefs[i];
2123                 name = PRVM_GetString(d->s_name);
2124                 if (name[strlen(name)-2] == '_')
2125                         continue;       // skip _x, _y, _z vars
2126                 switch(d->type & ~DEF_SAVEGLOBAL)
2127                 {
2128                 case ev_string:
2129                         strlcat(tempstring, "string   ", sizeof(tempstring));
2130                         break;
2131                 case ev_entity:
2132                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2133                         break;
2134                 case ev_function:
2135                         strlcat(tempstring, "function ", sizeof(tempstring));
2136                         break;
2137                 case ev_field:
2138                         strlcat(tempstring, "field    ", sizeof(tempstring));
2139                         break;
2140                 case ev_void:
2141                         strlcat(tempstring, "void     ", sizeof(tempstring));
2142                         break;
2143                 case ev_float:
2144                         strlcat(tempstring, "float    ", sizeof(tempstring));
2145                         break;
2146                 case ev_vector:
2147                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2148                         break;
2149                 case ev_pointer:
2150                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2151                         break;
2152                 default:
2153                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2154                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2155                         break;
2156                 }
2157                 if (strlen(name) > sizeof(tempstring2)-4)
2158                 {
2159                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2160                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2161                         tempstring2[sizeof(tempstring2)-1] = 0;
2162                         name = tempstring2;
2163                 }
2164                 strlcat(tempstring, name, sizeof(tempstring));
2165                 for (j = (int)strlen(name);j < 25;j++)
2166                         strlcat(tempstring, " ", sizeof(tempstring));
2167                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2168                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2169                 strlcat(tempstring, "\n", sizeof(tempstring));
2170                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2171                 {
2172                         Con_Print(tempstring);
2173                         tempstring[0] = 0;
2174                 }
2175                 if (counts[i])
2176                 {
2177                         used++;
2178                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2179                 }
2180         }
2181         Mem_Free(counts);
2182         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);
2183
2184         PRVM_End;
2185 }
2186
2187 void PRVM_Globals_f (void)
2188 {
2189         int i;
2190         const char *wildcard;
2191         int numculled;
2192                 numculled = 0;
2193         // TODO
2194         /*if (!sv.active)
2195         {
2196                 Con_Print("no progs loaded\n");
2197                 return;
2198         }*/
2199         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2200         {
2201                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2202                 return;
2203         }
2204
2205         PRVM_Begin;
2206         if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2207                 return;
2208
2209         if( Cmd_Argc() == 3)
2210                 wildcard = Cmd_Argv(2);
2211         else
2212                 wildcard = NULL;
2213
2214         Con_Printf("%s :", PRVM_NAME);
2215
2216         for (i = 0;i < prog->progs->numglobaldefs;i++)
2217         {
2218                 if(wildcard)
2219                         if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2220                         {
2221                                 numculled++;
2222                                 continue;
2223                         }
2224                 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2225         }
2226         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2227
2228         PRVM_End;
2229 }
2230
2231 /*
2232 ===============
2233 PRVM_Global
2234 ===============
2235 */
2236 void PRVM_Global_f(void)
2237 {
2238         ddef_t *global;
2239         if( Cmd_Argc() != 3 ) {
2240                 Con_Printf( "prvm_global <program name> <global name>\n" );
2241                 return;
2242         }
2243
2244         PRVM_Begin;
2245         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2246                 return;
2247
2248         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2249         if( !global )
2250                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2251         else
2252                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2253         PRVM_End;
2254 }
2255
2256 /*
2257 ===============
2258 PRVM_GlobalSet
2259 ===============
2260 */
2261 void PRVM_GlobalSet_f(void)
2262 {
2263         ddef_t *global;
2264         if( Cmd_Argc() != 4 ) {
2265                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2266                 return;
2267         }
2268
2269         PRVM_Begin;
2270         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2271                 return;
2272
2273         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2274         if( !global )
2275                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2276         else
2277                 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2278         PRVM_End;
2279 }
2280
2281 /*
2282 ===============
2283 PRVM_Init
2284 ===============
2285 */
2286 void PRVM_Init (void)
2287 {
2288         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2289         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2290         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2291         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2292         Cmd_AddCommand ("prvm_childprofile", PRVM_ChildProfile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu), sorted by time taken in function with child calls");
2293         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)");
2294         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)");
2295         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2296         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2297         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2298         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)");
2299         Cmd_AddCommand ("prvm_edictget", PRVM_ED_EdictGet_f, "retrieves the value of a specified property of a specified entity in the selected VM (server, client menu) into a cvar or to the console");
2300         Cmd_AddCommand ("prvm_globalget", PRVM_ED_GlobalGet_f, "retrieves the value of a specified global variable in the selected VM (server, client menu) into a cvar or to the console");
2301         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2302         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2303         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2304         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2305
2306         // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2307         prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2308
2309         Cvar_RegisterVariable (&prvm_traceqc);
2310         Cvar_RegisterVariable (&prvm_statementprofiling);
2311         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2312         Cvar_RegisterVariable (&prvm_leaktest);
2313         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2314         Cvar_RegisterVariable (&prvm_errordump);
2315         Cvar_RegisterVariable (&prvm_startupreuseedicttime);
2316
2317         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2318         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2319
2320         //VM_Cmd_Init();
2321 }
2322
2323 /*
2324 ===============
2325 PRVM_InitProg
2326 ===============
2327 */
2328 void PRVM_InitProg(int prognr)
2329 {
2330         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2331                 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2332
2333         prog = &prog_list[prognr];
2334
2335         if(prog->loaded)
2336                 PRVM_ResetProg();
2337
2338         memset(prog, 0, sizeof(prvm_prog_t));
2339         prog->starttime = Sys_DoubleTime();
2340
2341         prog->error_cmd = Host_Error;
2342         prog->leaktest_active = prvm_leaktest.integer != 0;
2343 }
2344
2345 int PRVM_GetProgNr(void)
2346 {
2347         return prog - prog_list;
2348 }
2349
2350 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2351 {
2352         return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2353 }
2354
2355 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2356 {
2357         _Mem_Free(buffer, filename, fileline);
2358 }
2359
2360 void _PRVM_FreeAll(const char *filename, int fileline)
2361 {
2362         prog->progs = NULL;
2363         prog->fielddefs = NULL;
2364         prog->functions = NULL;
2365         _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2366 }
2367
2368 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2369 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2370 {
2371         PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2372         return 0;
2373 }
2374
2375 /*
2376 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2377 {
2378         PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2379         return 0;
2380 }
2381
2382 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2383 {
2384         int n;
2385         n = e - prog->edicts;
2386         if ((unsigned int)n >= prog->limit_edicts)
2387                 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2388         return n;
2389 }
2390
2391 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2392 //{
2393 //      return e - prog->edicts;
2394 //}
2395
2396 //#define       PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2397 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2398 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2399 {
2400         int n;
2401         n = e - prog->edicts;
2402         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2403                 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2404         return n;// EXPERIMENTAL
2405         //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2406 }
2407 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2408 {
2409         if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2410                 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2411         return prog->edicts + n; // EXPERIMENTAL
2412         //return prog->edicts + ((n) / (progs->entityfields * 4));
2413 }
2414 */
2415
2416
2417 sizebuf_t vm_tempstringsbuf;
2418
2419 const char *PRVM_GetString(int num)
2420 {
2421         if (num >= 0)
2422         {
2423                 if (num < prog->stringssize)
2424                         return prog->strings + num;
2425                 else
2426 #if 1
2427                 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2428                 {
2429                         num -= prog->stringssize;
2430                         if (num < vm_tempstringsbuf.cursize)
2431                                 return (char *)vm_tempstringsbuf.data + num;
2432                         else
2433                         {
2434                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2435                                 return "";
2436                         }
2437                 }
2438                 else
2439 #endif
2440                 {
2441                         VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2442                         return "";
2443                 }
2444         }
2445         else
2446         {
2447                 num = -1 - num;
2448 #if 0
2449                 if (num >= (1<<30))
2450                 {
2451                         // special range reserved for tempstrings
2452                         num -= (1<<30);
2453                         if (num < vm_tempstringsbuf.cursize)
2454                                 return (char *)vm_tempstringsbuf.data + num;
2455                         else
2456                         {
2457                                 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2458                                 return "";
2459                         }
2460                 }
2461                 else
2462 #endif
2463                 if (num < prog->numknownstrings)
2464                 {
2465                         if (!prog->knownstrings[num])
2466                         {
2467                                 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2468                                 return "";
2469                         }
2470                         return prog->knownstrings[num];
2471                 }
2472                 else
2473                 {
2474                         VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2475                         return "";
2476                 }
2477         }
2478 }
2479
2480 int PRVM_SetEngineString(const char *s)
2481 {
2482         int i;
2483         if (!s)
2484                 return 0;
2485         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2486                 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2487         // if it's in the tempstrings area, use a reserved range
2488         // (otherwise we'd get millions of useless string offsets cluttering the database)
2489         if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2490 #if 1
2491                 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2492 #else
2493                 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2494 #endif
2495         // see if it's a known string address
2496         for (i = 0;i < prog->numknownstrings;i++)
2497                 if (prog->knownstrings[i] == s)
2498                         return -1 - i;
2499         // new unknown engine string
2500         if (developer.integer >= 200)
2501                 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2502         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2503                 if (!prog->knownstrings[i])
2504                         break;
2505         if (i >= prog->numknownstrings)
2506         {
2507                 if (i >= prog->maxknownstrings)
2508                 {
2509                         const char **oldstrings = prog->knownstrings;
2510                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2511                         const char **oldstrings_origin = prog->knownstrings_origin;
2512                         prog->maxknownstrings += 128;
2513                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2514                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2515                         if(prog->leaktest_active)
2516                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2517                         if (prog->numknownstrings)
2518                         {
2519                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2520                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2521                                 if(prog->leaktest_active)
2522                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2523                         }
2524                 }
2525                 prog->numknownstrings++;
2526         }
2527         prog->firstfreeknownstring = i + 1;
2528         prog->knownstrings[i] = s;
2529         prog->knownstrings_freeable[i] = false;
2530         if(prog->leaktest_active)
2531                 prog->knownstrings_origin[i] = NULL;
2532         return -1 - i;
2533 }
2534
2535 // temp string handling
2536
2537 // all tempstrings go into this buffer consecutively, and it is reset
2538 // whenever PRVM_ExecuteProgram returns to the engine
2539 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2540 //  restores it on return, so multiple recursive calls can share the same
2541 //  buffer)
2542 // the buffer size is automatically grown as needed
2543
2544 int PRVM_SetTempString(const char *s)
2545 {
2546         int size;
2547         char *t;
2548         if (!s)
2549                 return 0;
2550         size = (int)strlen(s) + 1;
2551         if (developer.integer >= 300)
2552                 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2553         if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2554         {
2555                 sizebuf_t old = vm_tempstringsbuf;
2556                 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2557                         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);
2558                 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2559                 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2560                         vm_tempstringsbuf.maxsize *= 2;
2561                 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2562                 {
2563                         if (developer.integer >= 100)
2564                                 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2565                         vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2566                         if (old.cursize)
2567                                 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2568                         if (old.data)
2569                                 Mem_Free(old.data);
2570                 }
2571         }
2572         t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2573         memcpy(t, s, size);
2574         vm_tempstringsbuf.cursize += size;
2575         return PRVM_SetEngineString(t);
2576 }
2577
2578 int PRVM_AllocString(size_t bufferlength, char **pointer)
2579 {
2580         int i;
2581         if (!bufferlength)
2582                 return 0;
2583         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2584                 if (!prog->knownstrings[i])
2585                         break;
2586         if (i >= prog->numknownstrings)
2587         {
2588                 if (i >= prog->maxknownstrings)
2589                 {
2590                         const char **oldstrings = prog->knownstrings;
2591                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2592                         const char **oldstrings_origin = prog->knownstrings_origin;
2593                         prog->maxknownstrings += 128;
2594                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2595                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2596                         if(prog->leaktest_active)
2597                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2598                         if (prog->numknownstrings)
2599                         {
2600                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2601                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2602                                 if(prog->leaktest_active)
2603                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2604                         }
2605                         // TODO why not Mem_Free the old ones?
2606                 }
2607                 prog->numknownstrings++;
2608         }
2609         prog->firstfreeknownstring = i + 1;
2610         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2611         prog->knownstrings_freeable[i] = true;
2612         if(prog->leaktest_active)
2613                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2614         if (pointer)
2615                 *pointer = (char *)(prog->knownstrings[i]);
2616         return -1 - i;
2617 }
2618
2619 void PRVM_FreeString(int num)
2620 {
2621         if (num == 0)
2622                 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2623         else if (num >= 0 && num < prog->stringssize)
2624                 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2625         else if (num < 0 && num >= -prog->numknownstrings)
2626         {
2627                 num = -1 - num;
2628                 if (!prog->knownstrings[num])
2629                         PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2630                 if (!prog->knownstrings_freeable[num])
2631                         PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2632                 PRVM_Free((char *)prog->knownstrings[num]);
2633                 if(prog->leaktest_active)
2634                         if(prog->knownstrings_origin[num])
2635                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
2636                 prog->knownstrings[num] = NULL;
2637                 prog->knownstrings_freeable[num] = false;
2638                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2639         }
2640         else
2641                 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2642 }
2643
2644 static qboolean PRVM_IsStringReferenced(string_t string)
2645 {
2646         int i, j;
2647
2648         for (i = 0;i < prog->progs->numglobaldefs;i++)
2649         {
2650                 ddef_t *d = &prog->globaldefs[i];
2651                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2652                         continue;
2653                 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2654                         return true;
2655         }
2656
2657         for(j = 0; j < prog->num_edicts; ++j)
2658         {
2659                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2660                 if (ed->priv.required->free)
2661                         continue;
2662                 for (i=0; i<prog->progs->numfielddefs; ++i)
2663                 {
2664                         ddef_t *d = &prog->fielddefs[i];
2665                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2666                                 continue;
2667                         if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2668                                 return true;
2669                 }
2670         }
2671
2672         return false;
2673 }
2674
2675 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2676 {
2677         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2678                 return true; // world or clients
2679         switch(prog - prog_list)
2680         {
2681                 case PRVM_SERVERPROG:
2682                         {
2683                                 entvars_t *ev = edict->fields.server;
2684                                 if(ev->solid) // can block other stuff, or is a trigger?
2685                                         return true;
2686                                 if(ev->modelindex) // visible ent?
2687                                         return true;
2688                                 if(ev->effects) // particle effect?
2689                                         return true;
2690                                 if(ev->think) // has a think function?
2691                                         if(ev->nextthink > 0) // that actually will eventually run?
2692                                                 return true;
2693                                 if(ev->takedamage)
2694                                         return true;
2695                                 if(*prvm_leaktest_ignore_classnames.string)
2696                                 {
2697                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2698                                                 return true;
2699                                 }
2700                         }
2701                         break;
2702                 case PRVM_CLIENTPROG:
2703                         {
2704                                 // TODO someone add more stuff here
2705                                 cl_entvars_t *ev = edict->fields.client;
2706                                 if(ev->entnum) // csqc networked
2707                                         return true;
2708                                 if(ev->modelindex) // visible ent?
2709                                         return true;
2710                                 if(ev->effects) // particle effect?
2711                                         return true;
2712                                 if(ev->think) // has a think function?
2713                                         if(ev->nextthink > 0) // that actually will eventually run?
2714                                                 return true;
2715                                 if(*prvm_leaktest_ignore_classnames.string)
2716                                 {
2717                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2718                                                 return true;
2719                                 }
2720                         }
2721                         break;
2722                 case PRVM_MENUPROG:
2723                         // menu prog does not have classnames
2724                         break;
2725         }
2726         return false;
2727 }
2728
2729 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2730 {
2731         int i, j;
2732         int edictnum = PRVM_NUM_FOR_EDICT(edict);
2733         const char *targetname = NULL;
2734
2735         switch(prog - prog_list)
2736         {
2737                 case PRVM_SERVERPROG:
2738                         targetname = PRVM_GetString(edict->fields.server->targetname);
2739                         break;
2740         }
2741
2742         if(targetname)
2743                 if(!*targetname) // ""
2744                         targetname = NULL;
2745
2746         for (i = 0;i < prog->progs->numglobaldefs;i++)
2747         {
2748                 ddef_t *d = &prog->globaldefs[i];
2749                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2750                         continue;
2751                 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2752                         return true;
2753         }
2754
2755         for(j = 0; j < prog->num_edicts; ++j)
2756         {
2757                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2758                 if (ed->priv.required->mark < mark)
2759                         continue;
2760                 if(ed == edict)
2761                         continue;
2762                 if(targetname)
2763                 {
2764                         const char *target = PRVM_GetString(ed->fields.server->target);
2765                         if(target)
2766                                 if(!strcmp(target, targetname))
2767                                         return true;
2768                 }
2769                 for (i=0; i<prog->progs->numfielddefs; ++i)
2770                 {
2771                         ddef_t *d = &prog->fielddefs[i];
2772                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2773                                 continue;
2774                         if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2775                                 return true;
2776                 }
2777         }
2778
2779         return false;
2780 }
2781
2782 static void PRVM_MarkReferencedEdicts(void)
2783 {
2784         int j;
2785         qboolean found_new;
2786         int stage;
2787
2788         for(j = 0; j < prog->num_edicts; ++j)
2789         {
2790                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2791                 if(ed->priv.required->free)
2792                         continue;
2793                 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2794         }
2795
2796         stage = 1;
2797         do
2798         {
2799                 found_new = false;
2800                 for(j = 0; j < prog->num_edicts; ++j)
2801                 {
2802                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2803                         if(ed->priv.required->free)
2804                                 continue;
2805                         if(ed->priv.required->mark)
2806                                 continue;
2807                         if(PRVM_IsEdictReferenced(ed, stage))
2808                         {
2809                                 ed->priv.required->mark = stage + 1;
2810                                 found_new = true;
2811                         }
2812                 }
2813                 ++stage;
2814         }
2815         while(found_new);
2816         Con_DPrintf("leak check used %d stages to find all references\n", stage);
2817 }
2818
2819 void PRVM_LeakTest(void)
2820 {
2821         int i, j;
2822         qboolean leaked = false;
2823
2824         if(!prog->leaktest_active)
2825                 return;
2826
2827         // 1. Strings
2828         for (i = 0; i < prog->numknownstrings; ++i)
2829         {
2830                 if(prog->knownstrings[i])
2831                 if(prog->knownstrings_freeable[i])
2832                 if(prog->knownstrings_origin[i])
2833                 if(!PRVM_IsStringReferenced(-1 - i))
2834                 {
2835                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2836                         leaked = true;
2837                 }
2838         }
2839
2840         // 2. Edicts
2841         PRVM_MarkReferencedEdicts();
2842         for(j = 0; j < prog->num_edicts; ++j)
2843         {
2844                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2845                 if(ed->priv.required->free)
2846                         continue;
2847                 if(!ed->priv.required->mark)
2848                 if(ed->priv.required->allocation_origin)
2849                 {
2850                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
2851                         PRVM_ED_Print(ed, NULL);
2852                         Con_Print("\n");
2853                         leaked = true;
2854                 }
2855         }
2856
2857         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2858         {
2859                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2860                 if(stringbuffer)
2861                 if(stringbuffer->origin)
2862                 {
2863                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
2864                         leaked = true;
2865                 }
2866         }
2867
2868         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2869         {
2870                 if(prog->openfiles[i])
2871                 if(prog->openfiles_origin[i])
2872                 {
2873                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
2874                         leaked = true;
2875                 }
2876         }
2877
2878         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2879         {
2880                 if(prog->opensearches[i])
2881                 if(prog->opensearches_origin[i])
2882                 {
2883                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
2884                         leaked = true;
2885                 }
2886         }
2887
2888         if(!leaked)
2889                 Con_Printf("Congratulations. No leaks found.\n");
2890 }