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