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