]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
add the prvm_badvalue variable needed by the last commit
[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_GLOBALFIELDEDICT(prog->globaloffsets.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                 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1466                 {
1467                         string_t handle =  PRVM_EDICTFIELDSTRING(ent, prog->fieldoffsets.classname);
1468                         if (!handle)
1469                         {
1470                                 Con_Print("No classname for:\n");
1471                                 PRVM_ED_Print(ent, NULL);
1472                                 PRVM_ED_Free (ent);
1473                                 continue;
1474                         }
1475
1476                         // look for the spawn function
1477                         funcname = PRVM_GetString(handle);
1478                         func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1479                         if(!func)
1480                                 if(!PRVM_GLOBALFIELDFLOAT(prog->globaloffsets.require_spawnfunc_prefix))
1481                                         func = PRVM_ED_FindFunction (funcname);
1482
1483                         if (!func)
1484                         {
1485                                 // check for OnEntityNoSpawnFunction
1486                                 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1487                                 {
1488                                         // self = ent
1489                                         PRVM_GLOBALFIELDEDICT(prog->globaloffsets.self) = PRVM_EDICT_TO_PROG(ent);
1490                                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1491                                 }
1492                                 else
1493                                 {
1494                                         if (developer.integer > 0) // don't confuse non-developers with errors
1495                                         {
1496                                                 Con_Print("No spawn function for:\n");
1497                                                 PRVM_ED_Print(ent, NULL);
1498                                         }
1499                                         PRVM_ED_Free (ent);
1500                                         continue; // not included in "inhibited" count
1501                                 }
1502                         }
1503                         else
1504                         {
1505                                 // self = ent
1506                                 PRVM_GLOBALFIELDEDICT(prog->globaloffsets.self) = PRVM_EDICT_TO_PROG(ent);
1507                                 PRVM_ExecuteProgram (func - prog->functions, "");
1508                         }
1509                 }
1510
1511                 if(!ent->priv.required->free)
1512                 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1513                 {
1514                         // self = ent
1515                         PRVM_GLOBALFIELDEDICT(prog->globaloffsets.self) = PRVM_EDICT_TO_PROG(ent);
1516                         PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1517                 }
1518
1519                 spawned++;
1520                 if (ent->priv.required->free)
1521                         died++;
1522         }
1523
1524         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);
1525
1526         prvm_reuseedicts_always_allow = 0;
1527 }
1528
1529 void PRVM_FindOffsets(void)
1530 {
1531         // field and global searches use -1 for NULL
1532         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1533         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1534         // functions use 0 for NULL
1535         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1536
1537         // server and client qc use a lot of similar fields, so this is combined
1538         prog->fieldoffsets.SendEntity                     = PRVM_ED_FindFieldOffset("SendEntity");
1539         prog->fieldoffsets.SendFlags                      = PRVM_ED_FindFieldOffset("SendFlags");
1540         prog->fieldoffsets.Version                        = PRVM_ED_FindFieldOffset("Version");
1541         prog->fieldoffsets.alpha                          = PRVM_ED_FindFieldOffset("alpha");
1542         prog->fieldoffsets.ammo_cells1                    = PRVM_ED_FindFieldOffset("ammo_cells1");
1543         prog->fieldoffsets.ammo_lava_nails                = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1544         prog->fieldoffsets.ammo_multi_rockets             = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1545         prog->fieldoffsets.ammo_nails1                    = PRVM_ED_FindFieldOffset("ammo_nails1");
1546         prog->fieldoffsets.ammo_plasma                    = PRVM_ED_FindFieldOffset("ammo_plasma");
1547         prog->fieldoffsets.ammo_rockets1                  = PRVM_ED_FindFieldOffset("ammo_rockets1");
1548         prog->fieldoffsets.ammo_shells1                   = PRVM_ED_FindFieldOffset("ammo_shells1");
1549         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1550         prog->fieldoffsets.button3                        = PRVM_ED_FindFieldOffset("button3");
1551         prog->fieldoffsets.button4                        = PRVM_ED_FindFieldOffset("button4");
1552         prog->fieldoffsets.button5                        = PRVM_ED_FindFieldOffset("button5");
1553         prog->fieldoffsets.button6                        = PRVM_ED_FindFieldOffset("button6");
1554         prog->fieldoffsets.button7                        = PRVM_ED_FindFieldOffset("button7");
1555         prog->fieldoffsets.button8                        = PRVM_ED_FindFieldOffset("button8");
1556         prog->fieldoffsets.button9                        = PRVM_ED_FindFieldOffset("button9");
1557         prog->fieldoffsets.button10                       = PRVM_ED_FindFieldOffset("button10");
1558         prog->fieldoffsets.button11                       = PRVM_ED_FindFieldOffset("button11");
1559         prog->fieldoffsets.button12                       = PRVM_ED_FindFieldOffset("button12");
1560         prog->fieldoffsets.button13                       = PRVM_ED_FindFieldOffset("button13");
1561         prog->fieldoffsets.button14                       = PRVM_ED_FindFieldOffset("button14");
1562         prog->fieldoffsets.button15                       = PRVM_ED_FindFieldOffset("button15");
1563         prog->fieldoffsets.button16                       = PRVM_ED_FindFieldOffset("button16");
1564         prog->fieldoffsets.buttonchat                     = PRVM_ED_FindFieldOffset("buttonchat");
1565         prog->fieldoffsets.buttonuse                      = PRVM_ED_FindFieldOffset("buttonuse");
1566         prog->fieldoffsets.chain                          = PRVM_ED_FindFieldOffset("chain");
1567         prog->fieldoffsets.classname                      = PRVM_ED_FindFieldOffset("classname");
1568         prog->fieldoffsets.clientcamera                   = PRVM_ED_FindFieldOffset("clientcamera");
1569         prog->fieldoffsets.clientcolors                   = PRVM_ED_FindFieldOffset("clientcolors");
1570         prog->fieldoffsets.clientstatus                   = PRVM_ED_FindFieldOffset("clientstatus");
1571         prog->fieldoffsets.color                          = PRVM_ED_FindFieldOffset("color");
1572         prog->fieldoffsets.colormod                       = PRVM_ED_FindFieldOffset("colormod");
1573         prog->fieldoffsets.contentstransition             = PRVM_ED_FindFieldOffset("contentstransition");
1574         prog->fieldoffsets.cursor_active                  = PRVM_ED_FindFieldOffset("cursor_active");
1575         prog->fieldoffsets.cursor_screen                  = PRVM_ED_FindFieldOffset("cursor_screen");
1576         prog->fieldoffsets.cursor_trace_endpos            = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1577         prog->fieldoffsets.cursor_trace_ent               = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1578         prog->fieldoffsets.cursor_trace_start             = PRVM_ED_FindFieldOffset("cursor_trace_start");
1579         prog->fieldoffsets.customizeentityforclient       = PRVM_ED_FindFieldOffset("customizeentityforclient");
1580         prog->fieldoffsets.dimension_hit                  = PRVM_ED_FindFieldOffset("dimension_hit");
1581         prog->fieldoffsets.dimension_solid                = PRVM_ED_FindFieldOffset("dimension_solid");
1582         prog->fieldoffsets.disableclientprediction        = PRVM_ED_FindFieldOffset("disableclientprediction");
1583         prog->fieldoffsets.discardabledemo                = PRVM_ED_FindFieldOffset("discardabledemo");
1584         prog->fieldoffsets.dphitcontentsmask              = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1585         prog->fieldoffsets.drawonlytoclient               = PRVM_ED_FindFieldOffset("drawonlytoclient");
1586         prog->fieldoffsets.exteriormodeltoclient          = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1587         prog->fieldoffsets.fatness                        = PRVM_ED_FindFieldOffset("fatness");
1588         prog->fieldoffsets.forceshader                    = PRVM_ED_FindFieldOffset("forceshader");
1589         prog->fieldoffsets.frame                          = PRVM_ED_FindFieldOffset("frame");
1590         prog->fieldoffsets.frame1time                     = PRVM_ED_FindFieldOffset("frame1time");
1591         prog->fieldoffsets.frame2                         = PRVM_ED_FindFieldOffset("frame2");
1592         prog->fieldoffsets.frame2time                     = PRVM_ED_FindFieldOffset("frame2time");
1593         prog->fieldoffsets.frame3                         = PRVM_ED_FindFieldOffset("frame3");
1594         prog->fieldoffsets.frame3time                     = PRVM_ED_FindFieldOffset("frame3time");
1595         prog->fieldoffsets.frame4                         = PRVM_ED_FindFieldOffset("frame4");
1596         prog->fieldoffsets.frame4time                     = PRVM_ED_FindFieldOffset("frame4time");
1597         prog->fieldoffsets.fullbright                     = PRVM_ED_FindFieldOffset("fullbright");
1598         prog->fieldoffsets.glow_color                     = PRVM_ED_FindFieldOffset("glow_color");
1599         prog->fieldoffsets.glow_size                      = PRVM_ED_FindFieldOffset("glow_size");
1600         prog->fieldoffsets.glow_trail                     = PRVM_ED_FindFieldOffset("glow_trail");
1601         prog->fieldoffsets.glowmod                        = PRVM_ED_FindFieldOffset("glowmod");
1602         prog->fieldoffsets.gravity                        = PRVM_ED_FindFieldOffset("gravity");
1603         prog->fieldoffsets.groundentity                   = PRVM_ED_FindFieldOffset("groundentity");
1604         prog->fieldoffsets.hull                           = PRVM_ED_FindFieldOffset("hull");
1605         prog->fieldoffsets.ideal_yaw                      = PRVM_ED_FindFieldOffset("ideal_yaw");
1606         prog->fieldoffsets.idealpitch                     = PRVM_ED_FindFieldOffset("idealpitch");
1607         prog->fieldoffsets.items2                         = PRVM_ED_FindFieldOffset("items2");
1608         prog->fieldoffsets.lerpfrac                       = PRVM_ED_FindFieldOffset("lerpfrac");
1609         prog->fieldoffsets.lerpfrac3                      = PRVM_ED_FindFieldOffset("lerpfrac3");
1610         prog->fieldoffsets.lerpfrac4                      = PRVM_ED_FindFieldOffset("lerpfrac4");
1611         prog->fieldoffsets.light_lev                      = PRVM_ED_FindFieldOffset("light_lev");
1612         prog->fieldoffsets.message                        = PRVM_ED_FindFieldOffset("message");
1613         prog->fieldoffsets.modelflags                     = PRVM_ED_FindFieldOffset("modelflags");
1614         prog->fieldoffsets.movement                       = PRVM_ED_FindFieldOffset("movement");
1615         prog->fieldoffsets.movetypesteplandevent          = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1616         prog->fieldoffsets.netaddress                     = PRVM_ED_FindFieldOffset("netaddress");
1617         prog->fieldoffsets.nextthink                      = PRVM_ED_FindFieldOffset("nextthink");
1618         prog->fieldoffsets.nodrawtoclient                 = PRVM_ED_FindFieldOffset("nodrawtoclient");
1619         prog->fieldoffsets.pflags                         = PRVM_ED_FindFieldOffset("pflags");
1620         prog->fieldoffsets.ping                           = PRVM_ED_FindFieldOffset("ping");
1621         prog->fieldoffsets.ping_packetloss                = PRVM_ED_FindFieldOffset("ping_packetloss");
1622         prog->fieldoffsets.ping_movementloss              = PRVM_ED_FindFieldOffset("ping_movementloss");
1623         prog->fieldoffsets.pitch_speed                    = PRVM_ED_FindFieldOffset("pitch_speed");
1624         prog->fieldoffsets.playermodel                    = PRVM_ED_FindFieldOffset("playermodel");
1625         prog->fieldoffsets.playerskin                     = PRVM_ED_FindFieldOffset("playerskin");
1626         prog->fieldoffsets.pmodel                         = PRVM_ED_FindFieldOffset("pmodel");
1627         prog->fieldoffsets.punchvector                    = PRVM_ED_FindFieldOffset("punchvector");
1628         prog->fieldoffsets.renderamt                      = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1629         prog->fieldoffsets.renderflags                    = PRVM_ED_FindFieldOffset("renderflags");
1630         prog->fieldoffsets.rendermode                     = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1631         prog->fieldoffsets.scale                          = PRVM_ED_FindFieldOffset("scale");
1632         prog->fieldoffsets.shadertime                     = PRVM_ED_FindFieldOffset("shadertime");
1633         prog->fieldoffsets.skeletonindex                  = PRVM_ED_FindFieldOffset("skeletonindex");
1634         prog->fieldoffsets.style                          = PRVM_ED_FindFieldOffset("style");
1635         prog->fieldoffsets.tag_entity                     = PRVM_ED_FindFieldOffset("tag_entity");
1636         prog->fieldoffsets.tag_index                      = PRVM_ED_FindFieldOffset("tag_index");
1637         prog->fieldoffsets.think                          = PRVM_ED_FindFieldOffset("think");
1638         prog->fieldoffsets.viewmodelforclient             = PRVM_ED_FindFieldOffset("viewmodelforclient");
1639         prog->fieldoffsets.viewzoom                       = PRVM_ED_FindFieldOffset("viewzoom");
1640         prog->fieldoffsets.yaw_speed                      = PRVM_ED_FindFieldOffset("yaw_speed");
1641         prog->fieldoffsets.bouncefactor                   = PRVM_ED_FindFieldOffset("bouncefactor");
1642         prog->fieldoffsets.bouncestop                     = PRVM_ED_FindFieldOffset("bouncestop");
1643         prog->fieldoffsets.sendcomplexanimation           = PRVM_ED_FindFieldOffset("sendcomplexanimation");
1644
1645         prog->fieldoffsets.solid                          = PRVM_ED_FindFieldOffset("solid");
1646         prog->fieldoffsets.movetype                       = PRVM_ED_FindFieldOffset("movetype");
1647         prog->fieldoffsets.modelindex                     = PRVM_ED_FindFieldOffset("modelindex");
1648         prog->fieldoffsets.mins                           = PRVM_ED_FindFieldOffset("mins");
1649         prog->fieldoffsets.maxs                           = PRVM_ED_FindFieldOffset("maxs");
1650         prog->fieldoffsets.mass                           = PRVM_ED_FindFieldOffset("mass");
1651         prog->fieldoffsets.origin                         = PRVM_ED_FindFieldOffset("origin");
1652         prog->fieldoffsets.velocity                       = PRVM_ED_FindFieldOffset("velocity");
1653         //prog->fieldoffsets.axis_forward                   = PRVM_ED_FindFieldOffset("axis_forward");
1654         //prog->fieldoffsets.axis_left                      = PRVM_ED_FindFieldOffset("axis_left");
1655         //prog->fieldoffsets.axis_up                        = PRVM_ED_FindFieldOffset("axis_up");
1656         //prog->fieldoffsets.spinvelocity                   = PRVM_ED_FindFieldOffset("spinvelocity");
1657         prog->fieldoffsets.angles                         = PRVM_ED_FindFieldOffset("angles");
1658         prog->fieldoffsets.avelocity                      = PRVM_ED_FindFieldOffset("avelocity");
1659         prog->fieldoffsets.aiment                         = PRVM_ED_FindFieldOffset("aiment");
1660         prog->fieldoffsets.enemy                          = PRVM_ED_FindFieldOffset("enemy");
1661         prog->fieldoffsets.jointtype                      = PRVM_ED_FindFieldOffset("jointtype");
1662         prog->fieldoffsets.movedir                        = PRVM_ED_FindFieldOffset("movedir");
1663
1664         prog->fieldoffsets.camera_transform               = PRVM_ED_FindFieldOffset("camera_transform");
1665         prog->fieldoffsets.userwavefunc_param0            = PRVM_ED_FindFieldOffset("userwavefunc_param0");
1666         prog->fieldoffsets.userwavefunc_param1            = PRVM_ED_FindFieldOffset("userwavefunc_param1");
1667         prog->fieldoffsets.userwavefunc_param2            = PRVM_ED_FindFieldOffset("userwavefunc_param2");
1668         prog->fieldoffsets.userwavefunc_param3            = PRVM_ED_FindFieldOffset("userwavefunc_param3");
1669
1670         prog->fieldoffsets.crypto_keyfp                   = PRVM_ED_FindFieldOffset("crypto_keyfp");
1671         prog->fieldoffsets.crypto_mykeyfp                 = PRVM_ED_FindFieldOffset("crypto_mykeyfp");
1672         prog->fieldoffsets.crypto_idfp                    = PRVM_ED_FindFieldOffset("crypto_idfp");
1673         prog->fieldoffsets.crypto_encryptmethod           = PRVM_ED_FindFieldOffset("crypto_encryptmethod");
1674         prog->fieldoffsets.crypto_signmethod              = PRVM_ED_FindFieldOffset("crypto_signmethod");
1675
1676         prog->funcoffsets.CSQC_ConsoleCommand             = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1677         prog->funcoffsets.CSQC_Ent_Remove                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1678         prog->funcoffsets.CSQC_Ent_Spawn                  = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1679         prog->funcoffsets.CSQC_Ent_Update                 = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1680         prog->funcoffsets.CSQC_Event                      = PRVM_ED_FindFunctionOffset("CSQC_Event");
1681         prog->funcoffsets.CSQC_Event_Sound                = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1682         prog->funcoffsets.CSQC_Init                       = PRVM_ED_FindFunctionOffset("CSQC_Init");
1683         prog->funcoffsets.CSQC_InputEvent                 = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1684         prog->funcoffsets.CSQC_Parse_CenterPrint          = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1685         prog->funcoffsets.CSQC_Parse_Print                = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1686         prog->funcoffsets.CSQC_Parse_StuffCmd             = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1687         prog->funcoffsets.CSQC_Parse_TempEntity           = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1688         prog->funcoffsets.CSQC_Shutdown                   = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1689         prog->funcoffsets.CSQC_UpdateView                 = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1690         prog->funcoffsets.EndFrame                        = PRVM_ED_FindFunctionOffset("EndFrame");
1691         prog->funcoffsets.GameCommand                     = PRVM_ED_FindFunctionOffset("GameCommand");
1692         prog->funcoffsets.Gecko_Query                     = PRVM_ED_FindFunctionOffset("Gecko_Query");
1693         prog->funcoffsets.RestoreGame                     = PRVM_ED_FindFunctionOffset("RestoreGame");
1694         prog->funcoffsets.SV_ChangeTeam                   = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1695         prog->funcoffsets.SV_OnEntityNoSpawnFunction      = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1696         prog->funcoffsets.SV_OnEntityPostSpawnFunction    = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1697         prog->funcoffsets.SV_OnEntityPreSpawnFunction     = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1698         prog->funcoffsets.SV_ParseClientCommand           = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1699         prog->funcoffsets.SV_PausedTic                    = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1700         prog->funcoffsets.SV_PlayerPhysics                = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1701         prog->funcoffsets.SV_Shutdown                     = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1702         prog->funcoffsets.URI_Get_Callback                = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1703         prog->globaloffsets.SV_InitCmd                    = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1704         prog->globaloffsets.coop                          = PRVM_ED_FindGlobalOffset("coop");
1705         prog->globaloffsets.deathmatch                    = PRVM_ED_FindGlobalOffset("deathmatch");
1706         prog->globaloffsets.dmg_origin                    = PRVM_ED_FindGlobalOffset("dmg_origin");
1707         prog->globaloffsets.dmg_save                      = PRVM_ED_FindGlobalOffset("dmg_save");
1708         prog->globaloffsets.dmg_take                      = PRVM_ED_FindGlobalOffset("dmg_take");
1709         prog->globaloffsets.drawfont                      = PRVM_ED_FindGlobalOffset("drawfont");
1710         prog->globaloffsets.drawfontscale                 = PRVM_ED_FindGlobalOffset("drawfontscale");
1711         prog->globaloffsets.gettaginfo_forward            = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1712         prog->globaloffsets.gettaginfo_name               = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1713         prog->globaloffsets.gettaginfo_offset             = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1714         prog->globaloffsets.gettaginfo_parent             = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1715         prog->globaloffsets.gettaginfo_right              = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1716         prog->globaloffsets.gettaginfo_up                 = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1717         prog->globaloffsets.transparent_offset            = PRVM_ED_FindGlobalOffset("transparent_offset");
1718         prog->globaloffsets.intermission                  = PRVM_ED_FindGlobalOffset("intermission");
1719         prog->globaloffsets.require_spawnfunc_prefix      = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1720         prog->globaloffsets.sb_showscores                 = PRVM_ED_FindGlobalOffset("sb_showscores");
1721         prog->globaloffsets.self                          = PRVM_ED_FindGlobalOffset("self");
1722         prog->globaloffsets.serverdeltatime               = PRVM_ED_FindGlobalOffset("serverdeltatime");
1723         prog->globaloffsets.serverprevtime                = PRVM_ED_FindGlobalOffset("serverprevtime");
1724         prog->globaloffsets.servertime                    = PRVM_ED_FindGlobalOffset("servertime");
1725         prog->globaloffsets.time                          = PRVM_ED_FindGlobalOffset("time");
1726         prog->globaloffsets.trace_allsolid                = PRVM_ED_FindGlobalOffset("trace_allsolid");
1727         prog->globaloffsets.trace_dphitcontents           = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1728         prog->globaloffsets.trace_dphitq3surfaceflags     = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1729         prog->globaloffsets.trace_dphittexturename        = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1730         prog->globaloffsets.trace_dpstartcontents         = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1731         prog->globaloffsets.trace_endpos                  = PRVM_ED_FindGlobalOffset("trace_endpos");
1732         prog->globaloffsets.trace_ent                     = PRVM_ED_FindGlobalOffset("trace_ent");
1733         prog->globaloffsets.trace_fraction                = PRVM_ED_FindGlobalOffset("trace_fraction");
1734         prog->globaloffsets.trace_inopen                  = PRVM_ED_FindGlobalOffset("trace_inopen");
1735         prog->globaloffsets.trace_inwater                 = PRVM_ED_FindGlobalOffset("trace_inwater");
1736         prog->globaloffsets.trace_networkentity           = PRVM_ED_FindGlobalOffset("trace_networkentity");
1737         prog->globaloffsets.trace_plane_dist              = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1738         prog->globaloffsets.trace_plane_normal            = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1739         prog->globaloffsets.trace_startsolid              = PRVM_ED_FindGlobalOffset("trace_startsolid");
1740         prog->globaloffsets.v_forward                     = PRVM_ED_FindGlobalOffset("v_forward");
1741         prog->globaloffsets.v_right                       = PRVM_ED_FindGlobalOffset("v_right");
1742         prog->globaloffsets.v_up                          = PRVM_ED_FindGlobalOffset("v_up");
1743         prog->globaloffsets.view_angles                   = PRVM_ED_FindGlobalOffset("view_angles");
1744         prog->globaloffsets.view_punchangle               = PRVM_ED_FindGlobalOffset("view_punchangle");
1745         prog->globaloffsets.view_punchvector              = PRVM_ED_FindGlobalOffset("view_punchvector");
1746         prog->globaloffsets.worldstatus                   = PRVM_ED_FindGlobalOffset("worldstatus");
1747         prog->globaloffsets.particles_alphamin            = PRVM_ED_FindGlobalOffset("particles_alphamin");
1748         prog->globaloffsets.particles_alphamax            = PRVM_ED_FindGlobalOffset("particles_alphamax");
1749         prog->globaloffsets.particles_colormin            = PRVM_ED_FindGlobalOffset("particles_colormin");
1750         prog->globaloffsets.particles_colormax            = PRVM_ED_FindGlobalOffset("particles_colormax");
1751
1752         // menu qc only uses some functions, nothing else
1753         prog->funcoffsets.m_draw                          = PRVM_ED_FindFunctionOffset("m_draw");
1754         prog->funcoffsets.m_init                          = PRVM_ED_FindFunctionOffset("m_init");
1755         prog->funcoffsets.m_keydown                       = PRVM_ED_FindFunctionOffset("m_keydown");
1756         prog->funcoffsets.m_keyup                         = PRVM_ED_FindFunctionOffset("m_keyup");
1757         prog->funcoffsets.m_shutdown                      = PRVM_ED_FindFunctionOffset("m_shutdown");
1758         prog->funcoffsets.m_toggle                        = PRVM_ED_FindFunctionOffset("m_toggle");
1759         prog->funcoffsets.m_newmap                        = PRVM_ED_FindFunctionOffset("m_newmap");
1760 }
1761
1762 // not used
1763 /*
1764 typedef struct dpfield_s
1765 {
1766         int type;
1767         char *string;
1768 }
1769 dpfield_t;
1770
1771 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1772
1773 dpfield_t dpfields[] =
1774 {
1775 };
1776 */
1777
1778 /*
1779 ===============
1780 PRVM_ResetProg
1781 ===============
1782 */
1783
1784 #define PO_HASHSIZE 16384
1785 typedef struct po_string_s
1786 {
1787         char *key, *value;
1788         struct po_string_s *nextonhashchain;
1789 }
1790 po_string_t;
1791 typedef struct po_s
1792 {
1793         po_string_t *hashtable[PO_HASHSIZE];
1794 }
1795 po_t;
1796 void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1797 {
1798         for(;;)
1799         {
1800                 switch(*in)
1801                 {
1802                         case 0:
1803                                 *out++ = 0;
1804                                 return;
1805                         case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1806                         case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1807                         case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1808                         case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1809                         case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1810                         case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1811                         case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1812                         default:
1813                                 if(*in >= 0 && *in <= 0x1F)
1814                                 {
1815                                         if(outsize >= 4)
1816                                         {
1817                                                 *out++ = '\\';
1818                                                 *out++ = '0' + ((*in & 0700) >> 6);
1819                                                 *out++ = '0' + ((*in & 0070) >> 3);
1820                                                 *out++ = '0' + ((*in & 0007));
1821                                                 outsize -= 4;
1822                                         }
1823                                 }
1824                                 else
1825                                 {
1826                                         if(outsize >= 1)
1827                                         {
1828                                                 *out++ = *in;
1829                                                 outsize -= 1;
1830                                         }
1831                                 }
1832                                 break;
1833                 }
1834                 ++in;
1835         }
1836 }
1837 void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1838 {
1839         for(;;)
1840         {
1841                 switch(*in)
1842                 {
1843                         case 0:
1844                                 *out++ = 0;
1845                                 return;
1846                         case '\\':
1847                                 ++in;
1848                                 switch(*in)
1849                                 {
1850                                         case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1851                                         case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1852                                         case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1853                                         case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1854                                         case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1855                                         case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1856                                         case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1857                                         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1858                                                 if(outsize > 0) 
1859                                                         *out = *in - '0';
1860                                                 ++in;
1861                                                 if(*in >= '0' && *in <= '7')
1862                                                 {
1863                                                         if(outsize > 0)
1864                                                                 *out = (*out << 3) | (*in - '0');
1865                                                         ++in;
1866                                                 }
1867                                                 if(*in >= '0' && *in <= '7')
1868                                                 {
1869                                                         if(outsize > 0)
1870                                                                 *out = (*out << 3) | (*in - '0');
1871                                                         ++in;
1872                                                 }
1873                                                 --in;
1874                                                 if(outsize > 0)
1875                                                 {
1876                                                         ++out;
1877                                                         --outsize;
1878                                                 }
1879                                                 break;
1880                                         default:
1881                                                 if(outsize > 0) { *out++ = *in; --outsize; }
1882                                                 break;
1883                                 }
1884                                 break;
1885                         default:
1886                                 if(outsize > 0)
1887                                 {
1888                                         *out++ = *in;
1889                                         --outsize;
1890                                 }
1891                                 break;
1892                 }
1893                 ++in;
1894         }
1895 }
1896 po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
1897 {
1898         po_t *po;
1899         const char *p, *q;
1900         int mode;
1901         char inbuf[MAX_INPUTLINE];
1902         char decodedbuf[MAX_INPUTLINE];
1903         size_t decodedpos;
1904         int hashindex;
1905         po_string_t thisstr;
1906         const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
1907
1908         if(!buf)
1909                 return NULL;
1910
1911         memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1912
1913         po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1914         memset(po, 0, sizeof(*po));
1915
1916         p = buf;
1917         while(*p)
1918         {
1919                 if(*p == '#')
1920                 {
1921                         // skip to newline
1922                         p = strchr(p, '\n');
1923                         if(!p)
1924                                 break;
1925                         ++p;
1926                         continue;
1927                 }
1928                 if(*p == '\r' || *p == '\n')
1929                 {
1930                         ++p;
1931                         continue;
1932                 }
1933                 if(!strncmp(p, "msgid \"", 7))
1934                 {
1935                         mode = 0;
1936                         p += 6;
1937                 }
1938                 else if(!strncmp(p, "msgstr \"", 8))
1939                 {
1940                         mode = 1;
1941                         p += 7;
1942                 }
1943                 else
1944                 {
1945                         p = strchr(p, '\n');
1946                         if(!p)
1947                                 break;
1948                         ++p;
1949                         continue;
1950                 }
1951                 decodedpos = 0;
1952                 while(*p == '"')
1953                 {
1954                         ++p;
1955                         q = strchr(p, '\n');
1956                         if(!q)
1957                                 break;
1958                         if(*(q-1) == '\r')
1959                                 --q;
1960                         if(*(q-1) != '"')
1961                                 break;
1962                         if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1963                                 break;
1964                         strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1965                         PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1966                         decodedpos += strlen(decodedbuf + decodedpos);
1967                         if(*q == '\r')
1968                                 ++q;
1969                         if(*q == '\n')
1970                                 ++q;
1971                         p = q;
1972                 }
1973                 if(mode == 0)
1974                 {
1975                         if(thisstr.key)
1976                                 Mem_Free(thisstr.key);
1977                         thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1978                         memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1979                 }
1980                 else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1981                 {
1982                         thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1983                         memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1984                         hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1985                         thisstr.nextonhashchain = po->hashtable[hashindex];
1986                         po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1987                         memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1988                         memset(&thisstr, 0, sizeof(thisstr));
1989                 }
1990         }
1991         
1992         Mem_Free((char *) buf);
1993         return po;
1994 }
1995 const char *PRVM_PO_Lookup(po_t *po, const char *str)
1996 {
1997         int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1998         po_string_t *p = po->hashtable[hashindex];
1999         while(p)
2000         {
2001                 if(!strcmp(str, p->key))
2002                         return p->value;
2003                 p = p->nextonhashchain;
2004         }
2005         return NULL;
2006 }
2007 void PRVM_PO_Destroy(po_t *po)
2008 {
2009         int i;
2010         for(i = 0; i < PO_HASHSIZE; ++i)
2011         {
2012                 po_string_t *p = po->hashtable[i];
2013                 while(p)
2014                 {
2015                         po_string_t *q = p;
2016                         p = p->nextonhashchain;
2017                         Mem_Free(q->key);
2018                         Mem_Free(q->value);
2019                         Mem_Free(q);
2020                 }
2021         }
2022         Mem_Free(po);
2023 }
2024
2025 void PRVM_LeakTest(void);
2026 void PRVM_ResetProg(void)
2027 {
2028         PRVM_LeakTest();
2029         PRVM_GCALL(reset_cmd)();
2030         Mem_FreePool(&prog->progs_mempool);
2031         if(prog->po)
2032                 PRVM_PO_Destroy((po_t *) prog->po);
2033         memset(prog,0,sizeof(prvm_prog_t));
2034         prog->starttime = Sys_DoubleTime();
2035 }
2036
2037 /*
2038 ===============
2039 PRVM_LoadLNO
2040 ===============
2041 */
2042 void PRVM_LoadLNO( const char *progname ) {
2043         fs_offset_t filesize;
2044         unsigned char *lno;
2045         unsigned int *header;
2046         char filename[512];
2047
2048         FS_StripExtension( progname, filename, sizeof( filename ) );
2049         strlcat( filename, ".lno", sizeof( filename ) );
2050
2051         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
2052         if( !lno ) {
2053                 return;
2054         }
2055
2056 /*
2057 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
2058 <Spike>    SafeWrite (h, &version, sizeof(int));
2059 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
2060 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
2061 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
2062 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
2063 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
2064 */
2065         if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
2066         {
2067                 Mem_Free(lno);
2068                 return;
2069         }
2070
2071         header = (unsigned int *) lno;
2072         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
2073                 LittleLong( header[ 1 ] ) == 1 &&
2074                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
2075                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
2076                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
2077                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
2078         {
2079                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
2080                 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
2081         }
2082         Mem_Free( lno );
2083 }
2084
2085 /*
2086 ===============
2087 PRVM_LoadProgs
2088 ===============
2089 */
2090 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)
2091 {
2092         int i;
2093         dprograms_t *dprograms;
2094         dstatement_t *instatements;
2095         ddef_t *infielddefs;
2096         ddef_t *inglobaldefs;
2097         float *inglobals;
2098         dfunction_t *infunctions;
2099         char *instrings;
2100         fs_offset_t filesize;
2101         int requiredglobalspace;
2102         opcode_t op;
2103         int a;
2104         int b;
2105         int c;
2106
2107         if (prog->loaded)
2108                 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
2109
2110         dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2111         if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2112                 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
2113         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2114
2115         Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
2116
2117         requiredglobalspace = 0;
2118         for (i = 0;i < numrequiredglobals;i++)
2119                 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2120
2121         prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2122
2123 // byte swap the header
2124         prog->progs_version = LittleLong(dprograms->version);
2125         prog->progs_crc = LittleLong(dprograms->crc);
2126         if (prog->progs_version != PROG_VERSION)
2127                 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs_version, PROG_VERSION);
2128         if (prog->progs_crc != prog->headercrc && prog->progs_crc != prog->headercrc2)
2129                 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);
2130         instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2131         prog->progs_numstatements = LittleLong(dprograms->numstatements);
2132         inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2133         prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2134         infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2135         prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2136         infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2137         prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2138         instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2139         prog->progs_numstrings = LittleLong(dprograms->numstrings);
2140         inglobals = (float *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2141         prog->progs_numglobals = LittleLong(dprograms->numglobals);
2142         prog->progs_entityfields = LittleLong(dprograms->entityfields);
2143
2144         prog->numstatements = prog->progs_numstatements;
2145         prog->numglobaldefs = prog->progs_numglobaldefs;
2146         prog->numfielddefs = prog->progs_numfielddefs;
2147         prog->numfunctions = prog->progs_numfunctions;
2148         prog->numstrings = prog->progs_numstrings;
2149         prog->numglobals = prog->progs_numglobals;
2150         prog->entityfields = prog->progs_entityfields;
2151
2152         if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings >= (int)filesize)
2153                 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
2154         prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2155         memcpy(prog->strings, instrings, prog->progs_numstrings);
2156         prog->stringssize = prog->progs_numstrings;
2157
2158         prog->numknownstrings = 0;
2159         prog->maxknownstrings = 0;
2160         prog->knownstrings = NULL;
2161         prog->knownstrings_freeable = NULL;
2162
2163         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2164
2165         // we need to expand the globaldefs and fielddefs to include engine defs
2166         prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2167         prog->globals.generic = (float *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace) * sizeof(float));
2168         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2169         // we need to convert the statements to our memory format
2170         prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2171         // allocate space for profiling statement usage
2172         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2173         // functions need to be converted to the memory format
2174         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2175
2176         for (i = 0;i < prog->progs_numfunctions;i++)
2177         {
2178                 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2179                 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2180                 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2181                 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2182                 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2183                 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2184                 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2185                 if(prog->functions[i].first_statement >= prog->numstatements)
2186                         PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
2187                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2188         }
2189
2190         // copy the globaldefs to the new globaldefs list
2191         for (i=0 ; i<prog->numglobaldefs ; i++)
2192         {
2193                 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2194                 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2195                 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2196                 // TODO bounds check ofs, s_name
2197         }
2198
2199         // append the required globals
2200         for (i = 0;i < numrequiredglobals;i++)
2201         {
2202                 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2203                 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2204                 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(required_global[i].name);
2205                 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2206                         prog->numglobals += 3;
2207                 else
2208                         prog->numglobals++;
2209                 prog->numglobaldefs++;
2210         }
2211
2212         // copy the progs fields to the new fields list
2213         for (i = 0;i < prog->numfielddefs;i++)
2214         {
2215                 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2216                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2217                         PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
2218                 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2219                 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2220                 // TODO bounds check ofs, s_name
2221         }
2222
2223         // append the required fields
2224         for (i = 0;i < numrequiredfields;i++)
2225         {
2226                 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2227                 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2228                 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
2229                 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2230                         prog->entityfields += 3;
2231                 else
2232                         prog->entityfields++;
2233                 prog->numfielddefs++;
2234         }
2235
2236         // LordHavoc: TODO: reorder globals to match engine struct
2237         // LordHavoc: TODO: reorder fields to match engine struct
2238 #define remapglobal(index) (index)
2239 #define remapfield(index) (index)
2240
2241         // copy globals
2242         for (i = 0;i < prog->progs_numglobals;i++)
2243                 ((int *)prog->globals.generic)[remapglobal(i)] = LittleLong(((int *)inglobals)[i]);
2244
2245         // LordHavoc: TODO: support 32bit progs statement formats
2246         // copy, remap globals in statements, bounds check
2247         for (i = 0;i < prog->progs_numstatements;i++)
2248         {
2249                 op = (opcode_t)LittleShort(instatements[i].op);
2250                 a = (unsigned short)LittleShort(instatements[i].a);
2251                 b = (unsigned short)LittleShort(instatements[i].b);
2252                 c = (unsigned short)LittleShort(instatements[i].c);
2253                 switch (op)
2254                 {
2255                 case OP_IF:
2256                 case OP_IFNOT:
2257                         b = (short)b;
2258                         if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2259                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
2260                         prog->statements[i].op = op;
2261                         prog->statements[i].operand[0] = remapglobal(a);
2262                         prog->statements[i].operand[1] = -1;
2263                         prog->statements[i].operand[2] = -1;
2264                         prog->statements[i].jumpabsolute = i + b;
2265                         break;
2266                 case OP_GOTO:
2267                         a = (short)a;
2268                         if (a + i < 0 || a + i >= prog->progs_numstatements)
2269                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
2270                         prog->statements[i].op = op;
2271                         prog->statements[i].operand[0] = -1;
2272                         prog->statements[i].operand[1] = -1;
2273                         prog->statements[i].operand[2] = -1;
2274                         prog->statements[i].jumpabsolute = i + a;
2275                         break;
2276                 default:
2277                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, PRVM_NAME);
2278                 // global global global
2279                 case OP_ADD_F:
2280                 case OP_ADD_V:
2281                 case OP_SUB_F:
2282                 case OP_SUB_V:
2283                 case OP_MUL_F:
2284                 case OP_MUL_V:
2285                 case OP_MUL_FV:
2286                 case OP_MUL_VF:
2287                 case OP_DIV_F:
2288                 case OP_BITAND:
2289                 case OP_BITOR:
2290                 case OP_GE:
2291                 case OP_LE:
2292                 case OP_GT:
2293                 case OP_LT:
2294                 case OP_AND:
2295                 case OP_OR:
2296                 case OP_EQ_F:
2297                 case OP_EQ_V:
2298                 case OP_EQ_S:
2299                 case OP_EQ_E:
2300                 case OP_EQ_FNC:
2301                 case OP_NE_F:
2302                 case OP_NE_V:
2303                 case OP_NE_S:
2304                 case OP_NE_E:
2305                 case OP_NE_FNC:
2306                 case OP_ADDRESS:
2307                 case OP_LOAD_F:
2308                 case OP_LOAD_FLD:
2309                 case OP_LOAD_ENT:
2310                 case OP_LOAD_S:
2311                 case OP_LOAD_FNC:
2312                 case OP_LOAD_V:
2313                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2314                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2315                         prog->statements[i].op = op;
2316                         prog->statements[i].operand[0] = remapglobal(a);
2317                         prog->statements[i].operand[1] = remapglobal(b);
2318                         prog->statements[i].operand[2] = remapglobal(c);
2319                         prog->statements[i].jumpabsolute = -1;
2320                         break;
2321                 // global none global
2322                 case OP_NOT_F:
2323                 case OP_NOT_V:
2324                 case OP_NOT_S:
2325                 case OP_NOT_FNC:
2326                 case OP_NOT_ENT:
2327                         if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2328                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2329                         prog->statements[i].op = op;
2330                         prog->statements[i].operand[0] = remapglobal(a);
2331                         prog->statements[i].operand[1] = -1;
2332                         prog->statements[i].operand[2] = remapglobal(c);
2333                         prog->statements[i].jumpabsolute = -1;
2334                         break;
2335                 // 2 globals
2336                 case OP_STOREP_F:
2337                 case OP_STOREP_ENT:
2338                 case OP_STOREP_FLD:
2339                 case OP_STOREP_S:
2340                 case OP_STOREP_FNC:
2341                 case OP_STORE_F:
2342                 case OP_STORE_ENT:
2343                 case OP_STORE_FLD:
2344                 case OP_STORE_S:
2345                 case OP_STORE_FNC:
2346                 case OP_STATE:
2347                 case OP_STOREP_V:
2348                 case OP_STORE_V:
2349                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2350                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2351                         prog->statements[i].op = op;
2352                         prog->statements[i].operand[0] = remapglobal(a);
2353                         prog->statements[i].operand[1] = remapglobal(b);
2354                         prog->statements[i].operand[2] = -1;
2355                         prog->statements[i].jumpabsolute = -1;
2356                         break;
2357                 // 1 global
2358                 case OP_CALL0:
2359                 case OP_CALL1:
2360                 case OP_CALL2:
2361                 case OP_CALL3:
2362                 case OP_CALL4:
2363                 case OP_CALL5:
2364                 case OP_CALL6:
2365                 case OP_CALL7:
2366                 case OP_CALL8:
2367                 case OP_DONE:
2368                 case OP_RETURN:
2369                         if ( a >= prog->progs_numglobals)
2370                                 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2371                         prog->statements[i].op = op;
2372                         prog->statements[i].operand[0] = remapglobal(a);
2373                         prog->statements[i].operand[1] = -1;
2374                         prog->statements[i].operand[2] = -1;
2375                         prog->statements[i].jumpabsolute = -1;
2376                         break;
2377                 }
2378         }
2379         if(prog->numstatements < 1)
2380         {
2381                 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2382         }
2383         else switch(prog->statements[prog->numstatements - 1].op)
2384         {
2385                 case OP_RETURN:
2386                 case OP_GOTO:
2387                 case OP_DONE:
2388                         break;
2389                 default:
2390                         PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2391                         break;
2392         }
2393
2394         // we're done with the file now
2395         Mem_Free(dprograms);
2396         dprograms = NULL;
2397
2398         // check required functions
2399         for(i=0 ; i < numrequiredfunc ; i++)
2400                 if(PRVM_ED_FindFunction(required_func[i]) == 0)
2401                         PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
2402
2403         PRVM_LoadLNO(filename);
2404
2405         PRVM_Init_Exec();
2406
2407         if(*prvm_language.string)
2408         // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2409         // later idea: include a list of authorized .po file checksums with the csprogs
2410         {
2411                 qboolean deftrans = !!strcmp(PRVM_NAME, "client");
2412                 const char *realfilename = (strcmp(PRVM_NAME, "client") ? filename : csqc_progname.string);
2413                 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2414                 {
2415                         for (i=0 ; i<prog->numglobaldefs ; i++)
2416                         {
2417                                 const char *name;
2418                                 name = PRVM_GetString(prog->globaldefs[i].s_name);
2419                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2420                                 if(name && !strncmp(name, "dotranslate_", 12))
2421                                 {
2422                                         deftrans = false;
2423                                         break;
2424                                 }
2425                         }
2426                 }
2427                 if(!strcmp(prvm_language.string, "dump"))
2428                 {
2429                         qfile_t *f = FS_OpenRealFile(va("%s.pot", realfilename), "w", false);
2430                         Con_Printf("Dumping to %s.pot\n", realfilename);
2431                         if(f)
2432                         {
2433                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2434                                 {
2435                                         const char *name;
2436                                         name = PRVM_GetString(prog->globaldefs[i].s_name);
2437                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2438                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2439                                         {
2440                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2441                                                 const char *value = PRVM_GetString(val->string);
2442                                                 if(*value)
2443                                                 {
2444                                                         char buf[MAX_INPUTLINE];
2445                                                         PRVM_PO_UnparseString(buf, value, sizeof(buf));
2446                                                         FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2447                                                 }
2448                                         }
2449                                 }
2450                                 FS_Close(f);
2451                         }
2452                 }
2453                 else
2454                 {
2455                         po_t *po = PRVM_PO_Load(va("%s.%s.po", realfilename, prvm_language.string), prog->progs_mempool);
2456                         if(po)
2457                         {
2458                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2459                                 {
2460                                         const char *name;
2461                                         name = PRVM_GetString(prog->globaldefs[i].s_name);
2462                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2463                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2464                                         {
2465                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2466                                                 const char *value = PRVM_GetString(val->string);
2467                                                 if(*value)
2468                                                 {
2469                                                         value = PRVM_PO_Lookup(po, value);
2470                                                         if(value)
2471                                                                 val->string = PRVM_SetEngineString(value);
2472                                                 }
2473                                         }
2474                                 }
2475                         }
2476                 }
2477         }
2478
2479         for (i=0 ; i<prog->numglobaldefs ; i++)
2480         {
2481                 const char *name;
2482                 name = PRVM_GetString(prog->globaldefs[i].s_name);
2483                 //Con_Printf("found var %s\n", name);
2484                 if(name
2485                         && !strncmp(name, "autocvar_", 9)
2486                         && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2487                 )
2488                 {
2489                         prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2490                         cvar_t *cvar = Cvar_FindVar(name + 9);
2491                         //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, PRVM_NAME);
2492                         if(!cvar)
2493                         {
2494                                 const char *value;
2495                                 char buf[64];
2496                                 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, PRVM_NAME);
2497                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2498                                 {
2499                                         case ev_float:
2500                                                 if((float)((int)(val->_float)) == val->_float)
2501                                                         dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2502                                                 else
2503                                                         dpsnprintf(buf, sizeof(buf), "%.9g", val->_float);
2504                                                 value = buf;
2505                                                 break;
2506                                         case ev_vector:
2507                                                 dpsnprintf(buf, sizeof(buf), "%.9g %.9g %.9g", val->vector[0], val->vector[1], val->vector[2]); value = buf;
2508                                                 break;
2509                                         case ev_string:
2510                                                 value = PRVM_GetString(val->string);
2511                                                 break;
2512                                         default:
2513                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2514                                                 goto fail;
2515                                 }
2516                                 cvar = Cvar_Get(name + 9, value, 0, NULL);
2517                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2518                                 {
2519                                         val->string = PRVM_SetEngineString(cvar->string);
2520                                         cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2521                                 }
2522                                 if(!cvar)
2523                                         PRVM_ERROR("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, PRVM_NAME);
2524                                 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2525                                 cvar->globaldefindex[prog - prog_list] = i;
2526                         }
2527                         else if((cvar->flags & CVAR_PRIVATE) == 0)
2528                         {
2529                                 // MUST BE SYNCED WITH cvar.c Cvar_Set
2530                                 int j;
2531                                 const char *s;
2532                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2533                                 {
2534                                         case ev_float:
2535                                                 val->_float = cvar->value;
2536                                                 break;
2537                                         case ev_vector:
2538                                                 s = cvar->string;
2539                                                 VectorClear(val->vector);
2540                                                 for (j = 0;j < 3;j++)
2541                                                 {
2542                                                         while (*s && ISWHITESPACE(*s))
2543                                                                 s++;
2544                                                         if (!*s)
2545                                                                 break;
2546                                                         val->vector[j] = atof(s);
2547                                                         while (!ISWHITESPACE(*s))
2548                                                                 s++;
2549                                                         if (!*s)
2550                                                                 break;
2551                                                 }
2552                                                 break;
2553                                         case ev_string:
2554                                                 val->string = PRVM_SetEngineString(cvar->string);
2555                                                 cvar->globaldefindex_stringno[prog - prog_list] = val->string;
2556                                                 break;
2557                                         default:
2558                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, PRVM_NAME);
2559                                                 goto fail;
2560                                 }
2561                                 cvar->globaldefindex_progid[prog - prog_list] = prog->id;
2562                                 cvar->globaldefindex[prog - prog_list] = i;
2563                         }
2564                         else
2565                                 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, PRVM_NAME);
2566                 }
2567 fail:
2568                 ;
2569         }
2570
2571         prog->loaded = TRUE;
2572
2573         // set flags & ddef_ts in prog
2574
2575         prog->flag = 0;
2576
2577         PRVM_FindOffsets();
2578
2579         PRVM_GCALL(init_cmd)();
2580
2581         // init mempools
2582         PRVM_MEM_Alloc();
2583 }
2584
2585
2586 void PRVM_Fields_f (void)
2587 {
2588         int i, j, ednum, used, usedamount;
2589         int *counts;
2590         char tempstring[MAX_INPUTLINE], tempstring2[260];
2591         const char *name;
2592         prvm_edict_t *ed;
2593         ddef_t *d;
2594         int *v;
2595
2596         // TODO
2597         /*
2598         if (!sv.active)
2599         {
2600                 Con_Print("no progs loaded\n");
2601                 return;
2602         }
2603         */
2604
2605         if(Cmd_Argc() != 2)
2606         {
2607                 Con_Print("prvm_fields <program name>\n");
2608                 return;
2609         }
2610
2611         PRVM_Begin;
2612         if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2613                 return;
2614
2615         counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2616         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2617         {
2618                 ed = PRVM_EDICT_NUM(ednum);
2619                 if (ed->priv.required->free)
2620                         continue;
2621                 for (i = 1;i < prog->numfielddefs;i++)
2622                 {
2623                         d = &prog->fielddefs[i];
2624                         name = PRVM_GetString(d->s_name);
2625                         if (name[strlen(name)-2] == '_')
2626                                 continue;       // skip _x, _y, _z vars
2627                         v = (int *)(ed->fields.vp + d->ofs);
2628                         // if the value is still all 0, skip the field
2629                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2630                         {
2631                                 if (v[j])
2632                                 {
2633                                         counts[i]++;
2634                                         break;
2635                                 }
2636                         }
2637                 }
2638         }
2639         used = 0;
2640         usedamount = 0;
2641         tempstring[0] = 0;
2642         for (i = 0;i < prog->numfielddefs;i++)
2643         {
2644                 d = &prog->fielddefs[i];
2645                 name = PRVM_GetString(d->s_name);
2646                 if (name[strlen(name)-2] == '_')
2647                         continue;       // skip _x, _y, _z vars
2648                 switch(d->type & ~DEF_SAVEGLOBAL)
2649                 {
2650                 case ev_string:
2651                         strlcat(tempstring, "string   ", sizeof(tempstring));
2652                         break;
2653                 case ev_entity:
2654                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2655                         break;
2656                 case ev_function:
2657                         strlcat(tempstring, "function ", sizeof(tempstring));
2658                         break;
2659                 case ev_field:
2660                         strlcat(tempstring, "field    ", sizeof(tempstring));
2661                         break;
2662                 case ev_void:
2663                         strlcat(tempstring, "void     ", sizeof(tempstring));
2664                         break;
2665                 case ev_float:
2666                         strlcat(tempstring, "float    ", sizeof(tempstring));
2667                         break;
2668                 case ev_vector:
2669                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2670                         break;
2671                 case ev_pointer:
2672                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2673                         break;
2674                 default:
2675                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2676                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2677                         break;
2678                 }
2679                 if (strlen(name) > sizeof(tempstring2)-4)
2680                 {
2681                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2682                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2683                         tempstring2[sizeof(tempstring2)-1] = 0;
2684                         name = tempstring2;
2685                 }
2686                 strlcat(tempstring, name, sizeof(tempstring));
2687                 for (j = (int)strlen(name);j < 25;j++)
2688                         strlcat(tempstring, " ", sizeof(tempstring));
2689                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2690                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2691                 strlcat(tempstring, "\n", sizeof(tempstring));
2692                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2693                 {
2694                         Con_Print(tempstring);
2695                         tempstring[0] = 0;
2696                 }
2697                 if (counts[i])
2698                 {
2699                         used++;
2700                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2701                 }
2702         }
2703         Mem_Free(counts);
2704         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);
2705
2706         PRVM_End;
2707 }
2708
2709 void PRVM_Globals_f (void)
2710 {
2711         int i;
2712         const char *wildcard;
2713         int numculled;
2714                 numculled = 0;
2715         // TODO
2716         /*if (!sv.active)
2717         {
2718                 Con_Print("no progs loaded\n");
2719                 return;
2720         }*/
2721         if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2722         {
2723                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2724                 return;
2725         }
2726
2727         PRVM_Begin;
2728         if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2729                 return;
2730
2731         if( Cmd_Argc() == 3)
2732                 wildcard = Cmd_Argv(2);
2733         else
2734                 wildcard = NULL;
2735
2736         Con_Printf("%s :", PRVM_NAME);
2737
2738         for (i = 0;i < prog->numglobaldefs;i++)
2739         {
2740                 if(wildcard)
2741                         if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2742                         {
2743                                 numculled++;
2744                                 continue;
2745                         }
2746                 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2747         }
2748         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2749
2750         PRVM_End;
2751 }
2752
2753 /*
2754 ===============
2755 PRVM_Global
2756 ===============
2757 */
2758 void PRVM_Global_f(void)
2759 {
2760         ddef_t *global;
2761         if( Cmd_Argc() != 3 ) {
2762                 Con_Printf( "prvm_global <program name> <global name>\n" );
2763                 return;
2764         }
2765
2766         PRVM_Begin;
2767         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2768                 return;
2769
2770         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2771         if( !global )
2772                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2773         else
2774                 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs) ) );
2775         PRVM_End;
2776 }
2777
2778 /*
2779 ===============
2780 PRVM_GlobalSet
2781 ===============
2782 */
2783 void PRVM_GlobalSet_f(void)
2784 {
2785         ddef_t *global;
2786         if( Cmd_Argc() != 4 ) {
2787                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2788                 return;
2789         }
2790
2791         PRVM_Begin;
2792         if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2793                 return;
2794
2795         global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2796         if( !global )
2797                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2798         else
2799                 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2800         PRVM_End;
2801 }
2802
2803 /*
2804 ===============
2805 PRVM_Init
2806 ===============
2807 */
2808 void PRVM_Init (void)
2809 {
2810         Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2811         Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2812         Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2813         Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2814         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");
2815         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)");
2816         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)");
2817         Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2818         Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2819         Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2820         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)");
2821         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");
2822         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");
2823         Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2824         Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2825         Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2826         Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2827
2828         Cvar_RegisterVariable (&prvm_language);
2829         Cvar_RegisterVariable (&prvm_traceqc);
2830         Cvar_RegisterVariable (&prvm_statementprofiling);
2831         Cvar_RegisterVariable (&prvm_timeprofiling);
2832         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2833         Cvar_RegisterVariable (&prvm_leaktest);
2834         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2835         Cvar_RegisterVariable (&prvm_errordump);
2836         Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2837         Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2838
2839         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2840         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2841
2842         //VM_Cmd_Init();
2843 }
2844
2845 /*
2846 ===============
2847 PRVM_InitProg
2848 ===============
2849 */
2850 void PRVM_InitProg(int prognr)
2851 {
2852         static unsigned int progid = 0;
2853
2854         if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2855                 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2856
2857         prog = &prog_list[prognr];
2858
2859         if(prog->loaded)
2860                 PRVM_ResetProg();
2861
2862         memset(prog, 0, sizeof(prvm_prog_t));
2863         prog->starttime = Sys_DoubleTime();
2864         prog->id = ++progid;
2865
2866         prog->error_cmd = Host_Error;
2867         prog->leaktest_active = prvm_leaktest.integer != 0;
2868 }
2869
2870 int PRVM_GetProgNr(void)
2871 {
2872         return prog - prog_list;
2873 }
2874
2875 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2876 {
2877         return _Mem_Alloc(prog->progs_mempool, NULL, buffersize, 16, filename, fileline);
2878 }
2879
2880 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2881 {
2882         _Mem_Free(buffer, filename, fileline);
2883 }
2884
2885 void _PRVM_FreeAll(const char *filename, int fileline)
2886 {
2887         prog->functions = NULL;
2888         prog->strings = NULL;
2889         prog->fielddefs = NULL;
2890         prog->globaldefs = NULL;
2891         prog->statements = NULL;
2892         // FIXME: what about knownstrings?
2893         _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2894 }
2895
2896 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2897 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, const char *filename, int fileline)
2898 {
2899         PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2900         return 0;
2901 }
2902
2903 sizebuf_t vm_tempstringsbuf;
2904 #define PRVM_KNOWNSTRINGBASE 0x40000000
2905
2906 const char *PRVM_GetString(int num)
2907 {
2908         if (num < 0)
2909         {
2910                 // invalid
2911                 VM_Warning("PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2912                 return "";
2913         }
2914         else if (num < prog->stringssize)
2915         {
2916                 // constant string from progs.dat
2917                 return prog->strings + num;
2918         }
2919         else if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2920         {
2921                 // tempstring returned by engine to QC (becomes invalid after returning to engine)
2922                 num -= prog->stringssize;
2923                 if (num < vm_tempstringsbuf.cursize)
2924                         return (char *)vm_tempstringsbuf.data + num;
2925                 else
2926                 {
2927                         VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2928                         return "";
2929                 }
2930         }
2931         else if (num & PRVM_KNOWNSTRINGBASE)
2932         {
2933                 // allocated string
2934                 num = num - PRVM_KNOWNSTRINGBASE;
2935                 if (num >= 0 && num < prog->numknownstrings)
2936                 {
2937                         if (!prog->knownstrings[num])
2938                         {
2939                                 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2940                                 return "";
2941                         }
2942                         return prog->knownstrings[num];
2943                 }
2944                 else
2945                 {
2946                         VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2947                         return "";
2948                 }
2949         }
2950         else
2951         {
2952                 // invalid string offset
2953                 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2954                 return "";
2955         }
2956 }
2957
2958 const char *PRVM_ChangeEngineString(int i, const char *s)
2959 {
2960         const char *old;
2961         i = i - PRVM_KNOWNSTRINGBASE;
2962         if(i < 0 || i >= prog->numknownstrings)
2963                 PRVM_ERROR("PRVM_ChangeEngineString: s is not an engine string");
2964         old = prog->knownstrings[i];
2965         prog->knownstrings[i] = s;
2966         return old;
2967 }
2968
2969 int PRVM_SetEngineString(const char *s)
2970 {
2971         int i;
2972         if (!s)
2973                 return 0;
2974         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2975                 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2976         // if it's in the tempstrings area, use a reserved range
2977         // (otherwise we'd get millions of useless string offsets cluttering the database)
2978         if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2979 #if 1
2980                 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2981 #endif
2982         // see if it's a known string address
2983         for (i = 0;i < prog->numknownstrings;i++)
2984                 if (prog->knownstrings[i] == s)
2985                         return PRVM_KNOWNSTRINGBASE + i;
2986         // new unknown engine string
2987         if (developer_insane.integer)
2988                 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
2989         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2990                 if (!prog->knownstrings[i])
2991                         break;
2992         if (i >= prog->numknownstrings)
2993         {
2994                 if (i >= prog->maxknownstrings)
2995                 {
2996                         const char **oldstrings = prog->knownstrings;
2997                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2998                         const char **oldstrings_origin = prog->knownstrings_origin;
2999                         prog->maxknownstrings += 128;
3000                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3001                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3002                         if(prog->leaktest_active)
3003                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3004                         if (prog->numknownstrings)
3005                         {
3006                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3007                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3008                                 if(prog->leaktest_active)
3009                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3010                         }
3011                 }
3012                 prog->numknownstrings++;
3013         }
3014         prog->firstfreeknownstring = i + 1;
3015         prog->knownstrings[i] = s;
3016         prog->knownstrings_freeable[i] = false;
3017         if(prog->leaktest_active)
3018                 prog->knownstrings_origin[i] = NULL;
3019         return PRVM_KNOWNSTRINGBASE + i;
3020 }
3021
3022 // temp string handling
3023
3024 // all tempstrings go into this buffer consecutively, and it is reset
3025 // whenever PRVM_ExecuteProgram returns to the engine
3026 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3027 //  restores it on return, so multiple recursive calls can share the same
3028 //  buffer)
3029 // the buffer size is automatically grown as needed
3030
3031 int PRVM_SetTempString(const char *s)
3032 {
3033         int size;
3034         char *t;
3035         if (!s)
3036                 return 0;
3037         size = (int)strlen(s) + 1;
3038         if (developer_insane.integer)
3039                 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
3040         if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
3041         {
3042                 sizebuf_t old = vm_tempstringsbuf;
3043                 if (vm_tempstringsbuf.cursize + size >= 1<<28)
3044                         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);
3045                 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
3046                 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
3047                         vm_tempstringsbuf.maxsize *= 2;
3048                 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
3049                 {
3050                         Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
3051                         vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
3052                         if (old.cursize)
3053                                 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
3054                         if (old.data)
3055                                 Mem_Free(old.data);
3056                 }
3057         }
3058         t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
3059         memcpy(t, s, size);
3060         vm_tempstringsbuf.cursize += size;
3061         return PRVM_SetEngineString(t);
3062 }
3063
3064 int PRVM_AllocString(size_t bufferlength, char **pointer)
3065 {
3066         int i;
3067         if (!bufferlength)
3068                 return 0;
3069         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3070                 if (!prog->knownstrings[i])
3071                         break;
3072         if (i >= prog->numknownstrings)
3073         {
3074                 if (i >= prog->maxknownstrings)
3075                 {
3076                         const char **oldstrings = prog->knownstrings;
3077                         const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
3078                         const char **oldstrings_origin = prog->knownstrings_origin;
3079                         prog->maxknownstrings += 128;
3080                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3081                         prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3082                         if(prog->leaktest_active)
3083                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3084                         if (prog->numknownstrings)
3085                         {
3086                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3087                                 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
3088                                 if(prog->leaktest_active)
3089                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3090                         }
3091                         if (oldstrings)
3092                                 Mem_Free((char **)oldstrings);
3093                         if (oldstrings_freeable)
3094                                 Mem_Free((unsigned char *)oldstrings_freeable);
3095                         if (oldstrings_origin)
3096                                 Mem_Free((char **)oldstrings_origin);
3097                 }
3098                 prog->numknownstrings++;
3099         }
3100         prog->firstfreeknownstring = i + 1;
3101         prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
3102         prog->knownstrings_freeable[i] = true;
3103         if(prog->leaktest_active)
3104                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
3105         if (pointer)
3106                 *pointer = (char *)(prog->knownstrings[i]);
3107         return PRVM_KNOWNSTRINGBASE + i;
3108 }
3109
3110 void PRVM_FreeString(int num)
3111 {
3112         if (num == 0)
3113                 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
3114         else if (num >= 0 && num < prog->stringssize)
3115                 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
3116         else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3117         {
3118                 num = num - PRVM_KNOWNSTRINGBASE;
3119                 if (!prog->knownstrings[num])
3120                         PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
3121                 if (!prog->knownstrings_freeable[num])
3122                         PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
3123                 PRVM_Free((char *)prog->knownstrings[num]);
3124                 if(prog->leaktest_active)
3125                         if(prog->knownstrings_origin[num])
3126                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
3127                 prog->knownstrings[num] = NULL;
3128                 prog->knownstrings_freeable[num] = false;
3129                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3130         }
3131         else
3132                 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
3133 }
3134
3135 static qboolean PRVM_IsStringReferenced(string_t string)
3136 {
3137         int i, j;
3138
3139         for (i = 0;i < prog->numglobaldefs;i++)
3140         {
3141                 ddef_t *d = &prog->globaldefs[i];
3142                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3143                         continue;
3144                 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3145                         return true;
3146         }
3147
3148         for(j = 0; j < prog->num_edicts; ++j)
3149         {
3150                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3151                 if (ed->priv.required->free)
3152                         continue;
3153                 for (i=0; i<prog->numfielddefs; ++i)
3154                 {
3155                         ddef_t *d = &prog->fielddefs[i];
3156                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3157                                 continue;
3158                         if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3159                                 return true;
3160                 }
3161         }
3162
3163         return false;
3164 }
3165
3166 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
3167 {
3168         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3169                 return true; // world or clients
3170         switch(prog - prog_list)
3171         {
3172                 case PRVM_SERVERPROG:
3173                         {
3174                                 entvars_t *ev = edict->fields.server;
3175                                 if(ev->solid) // can block other stuff, or is a trigger?
3176                                         return true;
3177                                 if(ev->modelindex) // visible ent?
3178                                         return true;
3179                                 if(ev->effects) // particle effect?
3180                                         return true;
3181                                 if(ev->think) // has a think function?
3182                                         if(ev->nextthink > 0) // that actually will eventually run?
3183                                                 return true;
3184                                 if(ev->takedamage)
3185                                         return true;
3186                                 if(*prvm_leaktest_ignore_classnames.string)
3187                                 {
3188                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3189                                                 return true;
3190                                 }
3191                         }
3192                         break;
3193                 case PRVM_CLIENTPROG:
3194                         {
3195                                 // TODO someone add more stuff here
3196                                 cl_entvars_t *ev = edict->fields.client;
3197                                 if(ev->entnum) // csqc networked
3198                                         return true;
3199                                 if(ev->modelindex) // visible ent?
3200                                         return true;
3201                                 if(ev->effects) // particle effect?
3202                                         return true;
3203                                 if(ev->think) // has a think function?
3204                                         if(ev->nextthink > 0) // that actually will eventually run?
3205                                                 return true;
3206                                 if(*prvm_leaktest_ignore_classnames.string)
3207                                 {
3208                                         if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
3209                                                 return true;
3210                                 }
3211                         }
3212                         break;
3213                 case PRVM_MENUPROG:
3214                         // menu prog does not have classnames
3215                         break;
3216         }
3217         return false;
3218 }
3219
3220 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
3221 {
3222         int i, j;
3223         int edictnum = PRVM_NUM_FOR_EDICT(edict);
3224         const char *targetname = NULL;
3225
3226         switch(prog - prog_list)
3227         {
3228                 case PRVM_SERVERPROG:
3229                         targetname = PRVM_GetString(edict->fields.server->targetname);
3230                         break;
3231         }
3232
3233         if(targetname)
3234                 if(!*targetname) // ""
3235                         targetname = NULL;
3236
3237         for (i = 0;i < prog->numglobaldefs;i++)
3238         {
3239                 ddef_t *d = &prog->globaldefs[i];
3240                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3241                         continue;
3242                 if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
3243                         return true;
3244         }
3245
3246         for(j = 0; j < prog->num_edicts; ++j)
3247         {
3248                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3249                 if (ed->priv.required->mark < mark)
3250                         continue;
3251                 if(ed == edict)
3252                         continue;
3253                 if(targetname)
3254                 {
3255                         const char *target = PRVM_GetString(ed->fields.server->target);
3256                         if(target)
3257                                 if(!strcmp(target, targetname))
3258                                         return true;
3259                 }
3260                 for (i=0; i<prog->numfielddefs; ++i)
3261                 {
3262                         ddef_t *d = &prog->fielddefs[i];
3263                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3264                                 continue;
3265                         if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3266                                 return true;
3267                 }
3268         }
3269
3270         return false;
3271 }
3272
3273 static void PRVM_MarkReferencedEdicts(void)
3274 {
3275         int j;
3276         qboolean found_new;
3277         int stage;
3278
3279         for(j = 0; j < prog->num_edicts; ++j)
3280         {
3281                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3282                 if(ed->priv.required->free)
3283                         continue;
3284                 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
3285         }
3286
3287         stage = 1;
3288         do
3289         {
3290                 found_new = false;
3291                 for(j = 0; j < prog->num_edicts; ++j)
3292                 {
3293                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3294                         if(ed->priv.required->free)
3295                                 continue;
3296                         if(ed->priv.required->mark)
3297                                 continue;
3298                         if(PRVM_IsEdictReferenced(ed, stage))
3299                         {
3300                                 ed->priv.required->mark = stage + 1;
3301                                 found_new = true;
3302                         }
3303                 }
3304                 ++stage;
3305         }
3306         while(found_new);
3307         Con_DPrintf("leak check used %d stages to find all references\n", stage);
3308 }
3309
3310 void PRVM_LeakTest(void)
3311 {
3312         int i, j;
3313         qboolean leaked = false;
3314
3315         if(!prog->leaktest_active)
3316                 return;
3317
3318         // 1. Strings
3319         for (i = 0; i < prog->numknownstrings; ++i)
3320         {
3321                 if(prog->knownstrings[i])
3322                 if(prog->knownstrings_freeable[i])
3323                 if(prog->knownstrings_origin[i])
3324                 if(!PRVM_IsStringReferenced(PRVM_KNOWNSTRINGBASE + i))
3325                 {
3326                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3327                         leaked = true;
3328                 }
3329         }
3330
3331         // 2. Edicts
3332         PRVM_MarkReferencedEdicts();
3333         for(j = 0; j < prog->num_edicts; ++j)
3334         {
3335                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3336                 if(ed->priv.required->free)
3337                         continue;
3338                 if(!ed->priv.required->mark)
3339                 if(ed->priv.required->allocation_origin)
3340                 {
3341                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3342                         PRVM_ED_Print(ed, NULL);
3343                         Con_Print("\n");
3344                         leaked = true;
3345                 }
3346         }
3347
3348         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3349         {
3350                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3351                 if(stringbuffer)
3352                 if(stringbuffer->origin)
3353                 {
3354                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3355                         leaked = true;
3356                 }
3357         }
3358
3359         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3360         {
3361                 if(prog->openfiles[i])
3362                 if(prog->openfiles_origin[i])
3363                 {
3364                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3365                         leaked = true;
3366                 }
3367         }
3368
3369         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3370         {
3371                 if(prog->opensearches[i])
3372                 if(prog->opensearches_origin[i])
3373                 {
3374                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3375                         leaked = true;
3376                 }
3377         }
3378
3379         if(!leaked)
3380                 Con_Printf("Congratulations. No leaks found.\n");
3381 }