]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
3cfad3dc801eff40de16d8bfb4bb265350bb5dcf
[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 = 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 == realtime)
241                 return true;
242         if(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(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 = 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 = 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 = 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 = 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 = 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 /*
2911 ===============
2912 PRVM_Init
2913 ===============
2914 */
2915 void PRVM_Init (void)
2916 {
2917         Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2918         Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2919         Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2920         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)");
2921         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");
2922         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)");
2923         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)");
2924         Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2925         Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2926         Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2927         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)");
2928         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");
2929         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");
2930         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)");
2931         Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2932         Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2933         Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2934         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");
2935         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");
2936         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");
2937
2938         Cvar_RegisterVariable (&prvm_language);
2939         Cvar_RegisterVariable (&prvm_traceqc);
2940         Cvar_RegisterVariable (&prvm_statementprofiling);
2941         Cvar_RegisterVariable (&prvm_timeprofiling);
2942         Cvar_RegisterVariable (&prvm_coverage);
2943         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2944         Cvar_RegisterVariable (&prvm_leaktest);
2945         Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2946         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2947         Cvar_RegisterVariable (&prvm_errordump);
2948         Cvar_RegisterVariable (&prvm_breakpointdump);
2949         Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2950         Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2951         Cvar_RegisterVariable (&prvm_garbagecollection_enable);
2952         Cvar_RegisterVariable (&prvm_garbagecollection_notify);
2953         Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
2954         Cvar_RegisterVariable (&prvm_garbagecollection_strings);
2955         Cvar_RegisterVariable (&prvm_stringdebug);
2956
2957         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2958         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2959
2960         //VM_Cmd_Init();
2961 }
2962
2963 /*
2964 ===============
2965 PRVM_InitProg
2966 ===============
2967 */
2968 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
2969 {
2970         PRVM_Prog_Reset(prog);
2971         prog->leaktest_active = prvm_leaktest.integer != 0;
2972         prog->console_cmd = cmd;
2973 }
2974
2975 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2976 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
2977 {
2978         prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
2979         return 0;
2980 }
2981
2982 #define PRVM_KNOWNSTRINGBASE 0x40000000
2983
2984 const char *PRVM_GetString(prvm_prog_t *prog, int num)
2985 {
2986         if (num < 0)
2987         {
2988                 // invalid
2989                 if (prvm_stringdebug.integer)
2990                         VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
2991                 return "";
2992         }
2993         else if (num < prog->stringssize)
2994         {
2995                 // constant string from progs.dat
2996                 return prog->strings + num;
2997         }
2998         else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
2999         {
3000                 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3001                 num -= prog->stringssize;
3002                 if (num < prog->tempstringsbuf.cursize)
3003                         return (char *)prog->tempstringsbuf.data + num;
3004                 else
3005                 {
3006                         if (prvm_stringdebug.integer)
3007                                 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3008                         return "";
3009                 }
3010         }
3011         else if (num & PRVM_KNOWNSTRINGBASE)
3012         {
3013                 // allocated string
3014                 num = num - PRVM_KNOWNSTRINGBASE;
3015                 if (num >= 0 && num < prog->numknownstrings)
3016                 {
3017                         if (!prog->knownstrings[num])
3018                         {
3019                                 if (prvm_stringdebug.integer)
3020                                         VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3021                                 return "";
3022                         }
3023                         // refresh the garbage collection on the string - this guards
3024                         // against a certain sort of repeated migration to earlier
3025                         // points in the scan that could otherwise result in the string
3026                         // being freed for being unused
3027                         prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3028                         return prog->knownstrings[num];
3029                 }
3030                 else
3031                 {
3032                         if (prvm_stringdebug.integer)
3033                                 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3034                         return "";
3035                 }
3036         }
3037         else
3038         {
3039                 // invalid string offset
3040                 if (prvm_stringdebug.integer)
3041                         VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3042                 return "";
3043         }
3044 }
3045
3046 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3047 {
3048         const char *old;
3049         i = i - PRVM_KNOWNSTRINGBASE;
3050         if (i < 0 || i >= prog->numknownstrings)
3051                 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3052         else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3053                 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3054         old = prog->knownstrings[i];
3055         prog->knownstrings[i] = s;
3056         return old;
3057 }
3058
3059 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3060 {
3061         if (i >= prog->numknownstrings)
3062         {
3063                 if (i >= prog->maxknownstrings)
3064                 {
3065                         const char **oldstrings = prog->knownstrings;
3066                         const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3067                         const char **oldstrings_origin = prog->knownstrings_origin;
3068                         prog->maxknownstrings += 128;
3069                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3070                         prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3071                         if (prog->leaktest_active)
3072                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3073                         if (prog->numknownstrings)
3074                         {
3075                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3076                                 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3077                                 if (prog->leaktest_active)
3078                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3079                         }
3080                 }
3081                 prog->numknownstrings++;
3082         }
3083         prog->firstfreeknownstring = i + 1;
3084         prog->knownstrings[i] = s;
3085         // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3086         prog->knownstrings_flags[i] = flags;
3087         if (prog->leaktest_active)
3088                 prog->knownstrings_origin[i] = NULL;
3089 }
3090
3091 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3092 {
3093         int i;
3094         if (!s)
3095                 return 0;
3096         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3097                 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3098         // if it's in the tempstrings area, use a reserved range
3099         // (otherwise we'd get millions of useless string offsets cluttering the database)
3100         if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3101                 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3102         // see if it's a known string address
3103         for (i = 0;i < prog->numknownstrings;i++)
3104                 if (prog->knownstrings[i] == s)
3105                         return PRVM_KNOWNSTRINGBASE + i;
3106         // new unknown engine string
3107         if (developer_insane.integer)
3108                 Con_DPrintf("new engine string %p = \"%s\"\n", s, s);
3109         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3110                 if (!prog->knownstrings[i])
3111                         break;
3112         PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3113         return PRVM_KNOWNSTRINGBASE + i;
3114 }
3115
3116 // temp string handling
3117
3118 // all tempstrings go into this buffer consecutively, and it is reset
3119 // whenever PRVM_ExecuteProgram returns to the engine
3120 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3121 //  restores it on return, so multiple recursive calls can share the same
3122 //  buffer)
3123 // the buffer size is automatically grown as needed
3124
3125 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3126 {
3127         int size;
3128         char *t;
3129         if (!s)
3130                 return 0;
3131         size = (int)strlen(s) + 1;
3132         if (developer_insane.integer)
3133                 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3134         if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3135         {
3136                 sizebuf_t old = prog->tempstringsbuf;
3137                 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3138                         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);
3139                 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3140                 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3141                         prog->tempstringsbuf.maxsize *= 2;
3142                 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3143                 {
3144                         Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3145                         prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3146                         if (old.data)
3147                         {
3148                                 if (old.cursize)
3149                                         memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3150                                 Mem_Free(old.data);
3151                         }
3152                 }
3153         }
3154         t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3155         memcpy(t, s, size);
3156         prog->tempstringsbuf.cursize += size;
3157         return PRVM_SetEngineString(prog, t);
3158 }
3159
3160 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3161 {
3162         int i;
3163         char *s;
3164         if (!bufferlength)
3165         {
3166                 if (pointer)
3167                         *pointer = NULL;
3168                 return 0;
3169         }
3170         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3171                 if (!prog->knownstrings[i])
3172                         break;
3173         s = (char *)PRVM_Alloc(bufferlength);
3174         PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3175         if(prog->leaktest_active)
3176                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3177         if (pointer)
3178                 *pointer = (char *)(prog->knownstrings[i]);
3179         return PRVM_KNOWNSTRINGBASE + i;
3180 }
3181
3182 void PRVM_FreeString(prvm_prog_t *prog, int num)
3183 {
3184         if (num == 0)
3185                 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3186         else if (num >= 0 && num < prog->stringssize)
3187                 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3188         else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3189         {
3190                 num = num - PRVM_KNOWNSTRINGBASE;
3191                 if (!prog->knownstrings[num])
3192                         prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3193                 if (!prog->knownstrings_flags[num])
3194                         prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3195                 PRVM_Free((char *)prog->knownstrings[num]);
3196                 if(prog->leaktest_active)
3197                         if(prog->knownstrings_origin[num])
3198                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
3199                 prog->knownstrings[num] = NULL;
3200                 prog->knownstrings_flags[num] = 0;
3201                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3202         }
3203         else
3204                 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3205 }
3206
3207 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3208 {
3209         int i, j;
3210
3211         for (i = 0;i < prog->numglobaldefs;i++)
3212         {
3213                 ddef_t *d = &prog->globaldefs[i];
3214                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3215                         continue;
3216                 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3217                         return true;
3218         }
3219
3220         for(j = 0; j < prog->num_edicts; ++j)
3221         {
3222                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3223                 if (ed->priv.required->free)
3224                         continue;
3225                 for (i=0; i<prog->numfielddefs; ++i)
3226                 {
3227                         ddef_t *d = &prog->fielddefs[i];
3228                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3229                                 continue;
3230                         if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3231                                 return true;
3232                 }
3233         }
3234
3235         return false;
3236 }
3237
3238 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3239 {
3240         char vabuf[1024];
3241         char vabuf2[1024];
3242         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3243                 return true; // world or clients
3244         if (edict->priv.required->freetime <= prog->inittime)
3245                 return true; // created during startup
3246         if (prog == SVVM_prog)
3247         {
3248                 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3249                         return true;
3250                 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3251                         return true;
3252                 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3253                         return true;
3254                 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3255                         if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3256                                 return true;
3257                 if(PRVM_serveredictfloat(edict, takedamage))
3258                         return true;
3259                 if(*prvm_leaktest_ignore_classnames.string)
3260                 {
3261                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3262                                 return true;
3263                 }
3264         }
3265         else if (prog == CLVM_prog)
3266         {
3267                 // TODO someone add more stuff here
3268                 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3269                         return true;
3270                 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3271                         return true;
3272                 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3273                         return true;
3274                 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3275                         if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3276                                 return true;
3277                 if(*prvm_leaktest_ignore_classnames.string)
3278                 {
3279                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3280                                 return true;
3281                 }
3282         }
3283         else
3284         {
3285                 // menu prog does not have classnames
3286         }
3287         return false;
3288 }
3289
3290 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3291 {
3292         int i, j;
3293         int edictnum = PRVM_NUM_FOR_EDICT(edict);
3294         const char *targetname = NULL;
3295
3296         if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3297                 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3298
3299         if(targetname)
3300                 if(!*targetname) // ""
3301                         targetname = NULL;
3302
3303         for(j = 0; j < prog->num_edicts; ++j)
3304         {
3305                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3306                 if (ed->priv.required->mark < mark)
3307                         continue;
3308                 if(ed == edict)
3309                         continue;
3310                 if(targetname)
3311                 {
3312                         const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3313                         if(target)
3314                                 if(!strcmp(target, targetname))
3315                                         return true;
3316                 }
3317                 for (i=0; i<prog->numfielddefs; ++i)
3318                 {
3319                         ddef_t *d = &prog->fielddefs[i];
3320                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3321                                 continue;
3322                         if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3323                                 return true;
3324                 }
3325         }
3326
3327         return false;
3328 }
3329
3330 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3331 {
3332         int i, j;
3333         qboolean found_new;
3334         int stage;
3335
3336         // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3337         stage = 1;
3338         for(j = 0; j < prog->num_edicts; ++j)
3339         {
3340                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3341                 if(ed->priv.required->free)
3342                         continue;
3343                 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3344         }
3345         for (i = 0;i < prog->numglobaldefs;i++)
3346         {
3347                 ddef_t *d = &prog->globaldefs[i];
3348                 prvm_edict_t *ed;
3349                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3350                         continue;
3351                 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3352                 if (i < 0 || j >= prog->max_edicts) {
3353                         Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3354                         continue;
3355                 }
3356                 ed = PRVM_EDICT_NUM(j);;
3357                 ed->priv.required->mark = stage;
3358         }
3359
3360         // Future stages: all entities that are referenced by an entity of the previous stage.
3361         do
3362         {
3363                 found_new = false;
3364                 for(j = 0; j < prog->num_edicts; ++j)
3365                 {
3366                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3367                         if(ed->priv.required->free)
3368                                 continue;
3369                         if(ed->priv.required->mark)
3370                                 continue;
3371                         if(PRVM_IsEdictReferenced(prog, ed, stage))
3372                         {
3373                                 ed->priv.required->mark = stage + 1;
3374                                 found_new = true;
3375                         }
3376                 }
3377                 ++stage;
3378         }
3379         while(found_new);
3380         Con_DPrintf("leak check used %d stages to find all references\n", stage);
3381 }
3382
3383 void PRVM_LeakTest(prvm_prog_t *prog)
3384 {
3385         int i, j;
3386         qboolean leaked = false;
3387
3388         if(!prog->leaktest_active)
3389                 return;
3390
3391         // 1. Strings
3392         for (i = 0; i < prog->numknownstrings; ++i)
3393         {
3394                 if(prog->knownstrings[i])
3395                 if(prog->knownstrings_flags[i])
3396                 if(prog->knownstrings_origin[i])
3397                 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3398                 {
3399                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3400                         leaked = true;
3401                 }
3402         }
3403
3404         // 2. Edicts
3405         PRVM_MarkReferencedEdicts(prog);
3406         for(j = 0; j < prog->num_edicts; ++j)
3407         {
3408                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3409                 if(ed->priv.required->free)
3410                         continue;
3411                 if(!ed->priv.required->mark)
3412                 if(ed->priv.required->allocation_origin)
3413                 {
3414                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3415                         PRVM_ED_Print(prog, ed, NULL);
3416                         Con_Print("\n");
3417                         leaked = true;
3418                 }
3419
3420                 ed->priv.required->mark = 0; // clear marks again when done
3421         }
3422
3423         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3424         {
3425                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3426                 if(stringbuffer)
3427                 if(stringbuffer->origin)
3428                 {
3429                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3430                         leaked = true;
3431                 }
3432         }
3433
3434         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3435         {
3436                 if(prog->openfiles[i])
3437                 if(prog->openfiles_origin[i])
3438                 {
3439                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3440                         leaked = true;
3441                 }
3442         }
3443
3444         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3445         {
3446                 if(prog->opensearches[i])
3447                 if(prog->opensearches_origin[i])
3448                 {
3449                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3450                         leaked = true;
3451                 }
3452         }
3453
3454         if(!leaked)
3455                 Con_Printf("Congratulations. No leaks found.\n");
3456 }
3457
3458 void PRVM_GarbageCollection(prvm_prog_t *prog)
3459 {
3460         int limit = prvm_garbagecollection_scan_limit.integer;
3461         prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3462         if (!prvm_garbagecollection_enable.integer)
3463                 return;
3464         // philosophy:
3465         // we like to limit how much scanning we do so it doesn't put a significant
3466         // burden on the cpu, so each of these are not complete scans, we also like
3467         // to have consistent cpu usage so we do a bit of work on each category of
3468         // leaked object every frame
3469         switch (gc->stage)
3470         {
3471         case PRVM_GC_START:
3472                 gc->stage++;
3473                 break;
3474         case PRVM_GC_GLOBALS_MARK:
3475                 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3476                 {
3477                         ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3478                         switch (d->type)
3479                         {
3480                         case ev_string:
3481                                 {
3482                                         prvm_int_t s = prog->globals.ip[d->ofs];
3483                                         if (s & PRVM_KNOWNSTRINGBASE)
3484                                         {
3485                                                 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3486                                                 if (!prog->knownstrings[num])
3487                                                 {
3488                                                         // invalid
3489                                                         Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3490                                                         prog->globals.ip[d->ofs] = 0;
3491                                                         continue;
3492                                                 }
3493                                                 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3494                                         }
3495                                 }
3496                                 break;
3497                         default:
3498                                 break;
3499                         }
3500                 }
3501                 if (gc->globals_mark_progress >= prog->numglobaldefs)
3502                         gc->stage++;
3503                 break;
3504         case PRVM_GC_FIELDS_MARK:
3505                 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3506                 {
3507                         ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3508                         switch (d->type)
3509                         {
3510                         case ev_string:
3511                                 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3512                                 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3513                                 {
3514                                         int entityindex = gc->fields_mark_progress_entity;
3515                                         prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3516                                         if (s & PRVM_KNOWNSTRINGBASE)
3517                                         {
3518                                                 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3519                                                 if (!prog->knownstrings[num])
3520                                                 {
3521                                                         // invalid
3522                                                         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));
3523                                                         prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3524                                                         continue;
3525                                                 }
3526                                                 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3527                                         }
3528                                 }
3529                                 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3530                                 {
3531                                         gc->fields_mark_progress_entity = 0;
3532                                         gc->fields_mark_progress++;
3533                                 }
3534                                 break;
3535                         default:
3536                                 gc->fields_mark_progress_entity = 0;
3537                                 gc->fields_mark_progress++;
3538                                 break;
3539                         }
3540                 }
3541                 if (gc->fields_mark_progress >= prog->numfielddefs)
3542                         gc->stage++;
3543                 break;
3544         case PRVM_GC_KNOWNSTRINGS_SWEEP:
3545                 // free any strzone'd strings that are not marked
3546                 if (!prvm_garbagecollection_strings.integer)
3547                 {
3548                         gc->stage++;
3549                         break;
3550                 }
3551                 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3552                 {
3553                         int num = gc->knownstrings_sweep_progress;
3554                         if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3555                         {
3556                                 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3557                                 {
3558                                         // string has been marked for pruning two passes in a row
3559                                         if (prvm_garbagecollection_notify.integer)
3560                                                 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3561                                         Mem_Free((char *)prog->knownstrings[num]);
3562                                         prog->knownstrings[num] = NULL;
3563                                         prog->knownstrings_flags[num] = 0;
3564                                         prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3565                                 }
3566                                 else
3567                                 {
3568                                         // mark it for pruning next pass
3569                                         prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3570                                 }
3571                         }
3572                 }
3573                 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3574                         gc->stage++;
3575                 break;
3576         case PRVM_GC_RESET:
3577         default:
3578                 memset(gc, 0, sizeof(*gc));
3579         }
3580 }