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