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