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