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