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