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