]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
Disable PRVM_64 until it can be developed further. Improves perf. in some cases
[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 %" PRVM_PRIi "() (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%" PRVM_PRIi " (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 %" PRVM_PRIi " (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 %" PRVM_PRIi "(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[128];
2395                                 int prec[3];
2396                                 float f;
2397                                 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2398                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2399                                 {
2400                                         case ev_float:
2401                                                 if((float)((int)(val->_float)) == val->_float)
2402                                                         dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2403                                                 else
2404                                                 {
2405                                                         // ftos_slow
2406                                                         f = val->_float;
2407                                                         for (int precision = 7; precision <= 9; ++precision) {
2408                                                                 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2409                                                                 if ((float)atof(buf) == f) {
2410                                                                         break;
2411                                                                 }
2412                                                         }
2413                                                 }
2414                                                 value = buf;
2415                                                 break;
2416                                         case ev_vector:
2417                                                 for (i = 0; i < 3; ++i)
2418                                                 {
2419                                                         prec[i] = 9;
2420                                                         f = val->vector[i];
2421                                                         for (int precision = 7; precision <= 9; ++precision) {
2422                                                                 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2423                                                                 if ((float)atof(buf) == f) {
2424                                                                         prec[i] = precision;
2425                                                                         break;
2426                                                                 }
2427                                                         }
2428                                                 }
2429                                                 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2430                                                 value = buf;
2431                                                 break;
2432                                         case ev_string:
2433                                                 value = PRVM_GetString(prog, val->string);
2434                                                 break;
2435                                         default:
2436                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2437                                                 goto fail;
2438                                 }
2439                                 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2440                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2441                                 {
2442                                         val->string = PRVM_SetEngineString(prog, cvar->string);
2443                                         cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2444                                 }
2445                                 if(!cvar)
2446                                         prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2447                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2448                         }
2449                         else if((cvar->flags & CVAR_PRIVATE) == 0)
2450                         {
2451                                 // MUST BE SYNCED WITH cvar.c Cvar_Set
2452                                 int j;
2453                                 const char *s;
2454                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2455                                 {
2456                                         case ev_float:
2457                                                 val->_float = cvar->value;
2458                                                 break;
2459                                         case ev_vector:
2460                                                 s = cvar->string;
2461                                                 VectorClear(val->vector);
2462                                                 for (j = 0;j < 3;j++)
2463                                                 {
2464                                                         while (*s && ISWHITESPACE(*s))
2465                                                                 s++;
2466                                                         if (!*s)
2467                                                                 break;
2468                                                         val->vector[j] = atof(s);
2469                                                         while (!ISWHITESPACE(*s))
2470                                                                 s++;
2471                                                         if (!*s)
2472                                                                 break;
2473                                                 }
2474                                                 break;
2475                                         case ev_string:
2476                                                 val->string = PRVM_SetEngineString(prog, cvar->string);
2477                                                 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2478                                                 break;
2479                                         default:
2480                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2481                                                 goto fail;
2482                                 }
2483                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2484                         }
2485                         else
2486                                 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2487                 }
2488 fail:
2489                 ;
2490         }
2491
2492         prog->loaded = true;
2493
2494         PRVM_UpdateBreakpoints(prog);
2495
2496         // set flags & ddef_ts in prog
2497
2498         prog->flag = 0;
2499
2500         PRVM_FindOffsets(prog);
2501
2502         prog->init_cmd(prog);
2503
2504         // init mempools
2505         PRVM_MEM_Alloc(prog);
2506
2507         // Inittime is at least the time when this function finished. However,
2508         // later events may bump it.
2509         prog->inittime = host.realtime;
2510 }
2511
2512
2513 static void PRVM_Fields_f(cmd_state_t *cmd)
2514 {
2515         prvm_prog_t *prog;
2516         int i, j, ednum, used, usedamount;
2517         int *counts;
2518         char tempstring[MAX_INPUTLINE], tempstring2[260];
2519         const char *name;
2520         prvm_edict_t *ed;
2521         ddef_t *d;
2522         prvm_eval_t *val;
2523
2524         // TODO
2525         /*
2526         if (!sv.active)
2527         {
2528                 Con_Print("no progs loaded\n");
2529                 return;
2530         }
2531         */
2532
2533         if(Cmd_Argc(cmd) != 2)
2534         {
2535                 Con_Print("prvm_fields <program name>\n");
2536                 return;
2537         }
2538
2539         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2540                 return;
2541
2542         counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2543         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2544         {
2545                 ed = PRVM_EDICT_NUM(ednum);
2546                 if (ed->priv.required->free)
2547                         continue;
2548                 for (i = 1;i < prog->numfielddefs;i++)
2549                 {
2550                         d = &prog->fielddefs[i];
2551                         name = PRVM_GetString(prog, d->s_name);
2552                         if (name[strlen(name)-2] == '_')
2553                                 continue;       // skip _x, _y, _z vars
2554                         val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2555                         // if the value is still all 0, skip the field
2556                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2557                         {
2558                                 if (val->ivector[j])
2559                                 {
2560                                         counts[i]++;
2561                                         break;
2562                                 }
2563                         }
2564                 }
2565         }
2566         used = 0;
2567         usedamount = 0;
2568         tempstring[0] = 0;
2569         for (i = 0;i < prog->numfielddefs;i++)
2570         {
2571                 d = &prog->fielddefs[i];
2572                 name = PRVM_GetString(prog, d->s_name);
2573                 if (name[strlen(name)-2] == '_')
2574                         continue;       // skip _x, _y, _z vars
2575                 switch(d->type & ~DEF_SAVEGLOBAL)
2576                 {
2577                 case ev_string:
2578                         strlcat(tempstring, "string   ", sizeof(tempstring));
2579                         break;
2580                 case ev_entity:
2581                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2582                         break;
2583                 case ev_function:
2584                         strlcat(tempstring, "function ", sizeof(tempstring));
2585                         break;
2586                 case ev_field:
2587                         strlcat(tempstring, "field    ", sizeof(tempstring));
2588                         break;
2589                 case ev_void:
2590                         strlcat(tempstring, "void     ", sizeof(tempstring));
2591                         break;
2592                 case ev_float:
2593                         strlcat(tempstring, "float    ", sizeof(tempstring));
2594                         break;
2595                 case ev_vector:
2596                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2597                         break;
2598                 case ev_pointer:
2599                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2600                         break;
2601                 default:
2602                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2603                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2604                         break;
2605                 }
2606                 if (strlen(name) > sizeof(tempstring2)-4)
2607                 {
2608                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2609                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2610                         tempstring2[sizeof(tempstring2)-1] = 0;
2611                         name = tempstring2;
2612                 }
2613                 strlcat(tempstring, name, sizeof(tempstring));
2614                 for (j = (int)strlen(name);j < 25;j++)
2615                         strlcat(tempstring, " ", sizeof(tempstring));
2616                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2617                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2618                 strlcat(tempstring, "\n", sizeof(tempstring));
2619                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2620                 {
2621                         Con_Print(tempstring);
2622                         tempstring[0] = 0;
2623                 }
2624                 if (counts[i])
2625                 {
2626                         used++;
2627                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2628                 }
2629         }
2630         Mem_Free(counts);
2631         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);
2632 }
2633
2634 static void PRVM_Globals_f(cmd_state_t *cmd)
2635 {
2636         prvm_prog_t *prog;
2637         int i;
2638         const char *wildcard;
2639         int numculled;
2640                 numculled = 0;
2641         // TODO
2642         /*if (!sv.active)
2643         {
2644                 Con_Print("no progs loaded\n");
2645                 return;
2646         }*/
2647         if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2648         {
2649                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2650                 return;
2651         }
2652
2653         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2654                 return;
2655
2656         if( Cmd_Argc(cmd) == 3)
2657                 wildcard = Cmd_Argv(cmd, 2);
2658         else
2659                 wildcard = NULL;
2660
2661         Con_Printf("%s :", prog->name);
2662
2663         for (i = 0;i < prog->numglobaldefs;i++)
2664         {
2665                 if(wildcard)
2666                         if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2667                         {
2668                                 numculled++;
2669                                 continue;
2670                         }
2671                 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2672         }
2673         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2674 }
2675
2676 /*
2677 ===============
2678 PRVM_Global
2679 ===============
2680 */
2681 static void PRVM_Global_f(cmd_state_t *cmd)
2682 {
2683         prvm_prog_t *prog;
2684         ddef_t *global;
2685         char valuebuf[MAX_INPUTLINE];
2686         if( Cmd_Argc(cmd) != 3 ) {
2687                 Con_Printf( "prvm_global <program name> <global name>\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                 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2699 }
2700
2701 /*
2702 ===============
2703 PRVM_GlobalSet
2704 ===============
2705 */
2706 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2707 {
2708         prvm_prog_t *prog;
2709         ddef_t *global;
2710         if( Cmd_Argc(cmd) != 4 ) {
2711                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2712                 return;
2713         }
2714
2715         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2716                 return;
2717
2718         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2719         if( !global )
2720                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2721         else
2722                 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2723 }
2724
2725 /*
2726 ======================
2727 Break- and Watchpoints
2728 ======================
2729 */
2730 typedef struct
2731 {
2732         char break_statement[256];
2733         char watch_global[256];
2734         int watch_edict;
2735         char watch_field[256];
2736 }
2737 debug_data_t;
2738 static debug_data_t debug_data[PRVM_PROG_MAX];
2739
2740 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2741 {
2742         char vabuf[1024];
2743         Con_Printf("PRVM_Breakpoint: %s\n", text);
2744         PRVM_PrintState(prog, stack_index);
2745         if (prvm_breakpointdump.integer)
2746                 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2747 }
2748
2749 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2750 {
2751         size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2752         if (memcmp(o, n, sz))
2753         {
2754                 char buf[1024];
2755                 char valuebuf_o[128];
2756                 char valuebuf_n[128];
2757                 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2758                 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2759                 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2760                 PRVM_Breakpoint(prog, stack_index, buf);
2761                 memcpy(o, n, sz);
2762         }
2763 }
2764
2765 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2766 {
2767         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2768         if (!prog->loaded)
2769                 return;
2770         if (debug->break_statement[0])
2771         {
2772                 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2773                 {
2774                         prog->break_statement = atoi(debug->break_statement);
2775                         prog->break_stack_index = 0;
2776                 }
2777                 else
2778                 {
2779                         mfunction_t *func;
2780                         func = PRVM_ED_FindFunction (prog, debug->break_statement);
2781                         if (!func)
2782                         {
2783                                 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2784                                 prog->break_statement = -1;
2785                         }
2786                         else
2787                         {
2788                                 prog->break_statement = func->first_statement;
2789                                 prog->break_stack_index = 1;
2790                         }
2791                 }
2792                 if (prog->break_statement >= -1)
2793                         Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2794         }
2795         else
2796                 prog->break_statement = -1;
2797
2798         if (debug->watch_global[0])
2799         {
2800                 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2801                 if( !global )
2802                 {
2803                         Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2804                         prog->watch_global_type = ev_void;
2805                 }
2806                 else
2807                 {
2808                         size_t sz = sizeof(prvm_vec_t) * ((global->type  & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2809                         prog->watch_global = global->ofs;
2810                         prog->watch_global_type = (etype_t)global->type;
2811                         memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2812                 }
2813                 if (prog->watch_global_type != ev_void)
2814                         Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2815         }
2816         else
2817                 prog->watch_global_type = ev_void;
2818
2819         if (debug->watch_field[0])
2820         {
2821                 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2822                 if( !field )
2823                 {
2824                         Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2825                         prog->watch_field_type = ev_void;
2826                 }
2827                 else
2828                 {
2829                         size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2830                         prog->watch_edict = debug->watch_edict;
2831                         prog->watch_field = field->ofs;
2832                         prog->watch_field_type = (etype_t)field->type;
2833                         if (prog->watch_edict < prog->num_edicts)
2834                                 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2835                         else
2836                                 memset(&prog->watch_edictfield_value, 0, sz);
2837                 }
2838                 if (prog->watch_edict != ev_void)
2839                         Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2840         }
2841         else
2842                 prog->watch_field_type = ev_void;
2843 }
2844
2845 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2846 {
2847         prvm_prog_t *prog;
2848
2849         if( Cmd_Argc(cmd) == 2 ) {
2850                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2851                         return;
2852                 {
2853                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2854                         debug->break_statement[0] = 0;
2855                 }
2856                 PRVM_UpdateBreakpoints(prog);
2857                 return;
2858         }
2859         if( Cmd_Argc(cmd) != 3 ) {
2860                 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2861                 return;
2862         }
2863
2864         if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2865                 return;
2866
2867         {
2868                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2869                 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2870         }
2871         PRVM_UpdateBreakpoints(prog);
2872 }
2873
2874 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2875 {
2876         prvm_prog_t *prog;
2877
2878         if( Cmd_Argc(cmd) == 2 ) {
2879                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2880                         return;
2881                 {
2882                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2883                         debug->watch_global[0] = 0;
2884                 }
2885                 PRVM_UpdateBreakpoints(prog);
2886                 return;
2887         }
2888         if( Cmd_Argc(cmd) != 3 ) {
2889                 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2890                 return;
2891         }
2892
2893         if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2894                 return;
2895
2896         {
2897                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2898                 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2899         }
2900         PRVM_UpdateBreakpoints(prog);
2901 }
2902
2903 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2904 {
2905         prvm_prog_t *prog;
2906
2907         if( Cmd_Argc(cmd) == 2 ) {
2908                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2909                         return;
2910                 {
2911                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2912                         debug->watch_field[0] = 0;
2913                 }
2914                 PRVM_UpdateBreakpoints(prog);
2915                 return;
2916         }
2917         if( Cmd_Argc(cmd) != 4 ) {
2918                 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2919                 return;
2920         }
2921
2922         if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2923                 return;
2924
2925         {
2926                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2927                 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2928                 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2929         }
2930         PRVM_UpdateBreakpoints(prog);
2931 }
2932
2933 /*
2934 ===============
2935 PRVM_Init
2936 ===============
2937 */
2938 void PRVM_Init (void)
2939 {
2940         Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2941         Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2942         Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2943         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)");
2944         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");
2945         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)");
2946         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)");
2947         Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2948         Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2949         Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2950         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)");
2951         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");
2952         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");
2953         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)");
2954         Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2955         Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2956         Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2957         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");
2958         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");
2959         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");
2960
2961         Cvar_RegisterVariable (&prvm_language);
2962         Cvar_RegisterVariable (&prvm_traceqc);
2963         Cvar_RegisterVariable (&prvm_statementprofiling);
2964         Cvar_RegisterVariable (&prvm_timeprofiling);
2965         Cvar_RegisterVariable (&prvm_coverage);
2966         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2967         Cvar_RegisterVariable (&prvm_leaktest);
2968         Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
2969         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2970         Cvar_RegisterVariable (&prvm_errordump);
2971         Cvar_RegisterVariable (&prvm_breakpointdump);
2972         Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
2973         Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
2974         Cvar_RegisterVariable (&prvm_garbagecollection_enable);
2975         Cvar_RegisterVariable (&prvm_garbagecollection_notify);
2976         Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
2977         Cvar_RegisterVariable (&prvm_garbagecollection_strings);
2978         Cvar_RegisterVariable (&prvm_stringdebug);
2979
2980         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2981         prvm_runawaycheck = !COM_CheckParm("-norunaway");
2982
2983         //VM_Cmd_Init();
2984 }
2985
2986 /*
2987 ===============
2988 PRVM_InitProg
2989 ===============
2990 */
2991 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
2992 {
2993         PRVM_Prog_Reset(prog);
2994         prog->leaktest_active = prvm_leaktest.integer != 0;
2995         prog->console_cmd = cmd;
2996 }
2997
2998 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2999 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3000 {
3001         prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3002         return 0;
3003 }
3004
3005 #define PRVM_KNOWNSTRINGBASE 0x40000000
3006
3007 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3008 {
3009         if (num < 0)
3010         {
3011                 // invalid
3012                 if (prvm_stringdebug.integer)
3013                         VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3014                 return "";
3015         }
3016         else if (num < prog->stringssize)
3017         {
3018                 // constant string from progs.dat
3019                 return prog->strings + num;
3020         }
3021         else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3022         {
3023                 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3024                 num -= prog->stringssize;
3025                 if (num < prog->tempstringsbuf.cursize)
3026                         return (char *)prog->tempstringsbuf.data + num;
3027                 else
3028                 {
3029                         if (prvm_stringdebug.integer)
3030                                 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3031                         return "";
3032                 }
3033         }
3034         else if (num & PRVM_KNOWNSTRINGBASE)
3035         {
3036                 // allocated string
3037                 num = num - PRVM_KNOWNSTRINGBASE;
3038                 if (num >= 0 && num < prog->numknownstrings)
3039                 {
3040                         if (!prog->knownstrings[num])
3041                         {
3042                                 if (prvm_stringdebug.integer)
3043                                         VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3044                                 return "";
3045                         }
3046                         // refresh the garbage collection on the string - this guards
3047                         // against a certain sort of repeated migration to earlier
3048                         // points in the scan that could otherwise result in the string
3049                         // being freed for being unused
3050                         prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3051                         return prog->knownstrings[num];
3052                 }
3053                 else
3054                 {
3055                         if (prvm_stringdebug.integer)
3056                                 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3057                         return "";
3058                 }
3059         }
3060         else
3061         {
3062                 // invalid string offset
3063                 if (prvm_stringdebug.integer)
3064                         VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3065                 return "";
3066         }
3067 }
3068
3069 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3070 {
3071         const char *old;
3072         i = i - PRVM_KNOWNSTRINGBASE;
3073         if (i < 0 || i >= prog->numknownstrings)
3074                 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3075         else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3076                 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3077         old = prog->knownstrings[i];
3078         prog->knownstrings[i] = s;
3079         return old;
3080 }
3081
3082 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3083 {
3084         if (i >= prog->numknownstrings)
3085         {
3086                 if (i >= prog->maxknownstrings)
3087                 {
3088                         const char **oldstrings = prog->knownstrings;
3089                         const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3090                         const char **oldstrings_origin = prog->knownstrings_origin;
3091                         prog->maxknownstrings += 128;
3092                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3093                         prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3094                         if (prog->leaktest_active)
3095                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3096                         if (prog->numknownstrings)
3097                         {
3098                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3099                                 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3100                                 if (prog->leaktest_active)
3101                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3102                         }
3103                 }
3104                 prog->numknownstrings++;
3105         }
3106         prog->firstfreeknownstring = i + 1;
3107         prog->knownstrings[i] = s;
3108         // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3109         prog->knownstrings_flags[i] = flags;
3110         if (prog->leaktest_active)
3111                 prog->knownstrings_origin[i] = NULL;
3112 }
3113
3114 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3115 {
3116         int i;
3117         if (!s)
3118                 return 0;
3119         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3120                 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3121         // if it's in the tempstrings area, use a reserved range
3122         // (otherwise we'd get millions of useless string offsets cluttering the database)
3123         if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3124                 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3125         // see if it's a known string address
3126         for (i = 0;i < prog->numknownstrings;i++)
3127                 if (prog->knownstrings[i] == s)
3128                         return PRVM_KNOWNSTRINGBASE + i;
3129         // new unknown engine string
3130         if (developer_insane.integer)
3131                 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3132         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3133                 if (!prog->knownstrings[i])
3134                         break;
3135         PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3136         return PRVM_KNOWNSTRINGBASE + i;
3137 }
3138
3139 // temp string handling
3140
3141 // all tempstrings go into this buffer consecutively, and it is reset
3142 // whenever PRVM_ExecuteProgram returns to the engine
3143 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3144 //  restores it on return, so multiple recursive calls can share the same
3145 //  buffer)
3146 // the buffer size is automatically grown as needed
3147
3148 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3149 {
3150         int size;
3151         char *t;
3152         if (!s)
3153                 return 0;
3154         size = (int)strlen(s) + 1;
3155         if (developer_insane.integer)
3156                 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3157         if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3158         {
3159                 sizebuf_t old = prog->tempstringsbuf;
3160                 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3161                         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);
3162                 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3163                 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3164                         prog->tempstringsbuf.maxsize *= 2;
3165                 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3166                 {
3167                         Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3168                         prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3169                         if (old.data)
3170                         {
3171                                 if (old.cursize)
3172                                         memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3173                                 Mem_Free(old.data);
3174                         }
3175                 }
3176         }
3177         t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3178         memcpy(t, s, size);
3179         prog->tempstringsbuf.cursize += size;
3180         return PRVM_SetEngineString(prog, t);
3181 }
3182
3183 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3184 {
3185         int i;
3186         char *s;
3187         if (!bufferlength)
3188         {
3189                 if (pointer)
3190                         *pointer = NULL;
3191                 return 0;
3192         }
3193         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3194                 if (!prog->knownstrings[i])
3195                         break;
3196         s = (char *)PRVM_Alloc(bufferlength);
3197         PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3198         if(prog->leaktest_active)
3199                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3200         if (pointer)
3201                 *pointer = (char *)(prog->knownstrings[i]);
3202         return PRVM_KNOWNSTRINGBASE + i;
3203 }
3204
3205 void PRVM_FreeString(prvm_prog_t *prog, int num)
3206 {
3207         if (num == 0)
3208                 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3209         else if (num >= 0 && num < prog->stringssize)
3210                 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3211         else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3212         {
3213                 num = num - PRVM_KNOWNSTRINGBASE;
3214                 if (!prog->knownstrings[num])
3215                         prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3216                 if (!prog->knownstrings_flags[num])
3217                         prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3218                 PRVM_Free((char *)prog->knownstrings[num]);
3219                 if(prog->leaktest_active)
3220                         if(prog->knownstrings_origin[num])
3221                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
3222                 prog->knownstrings[num] = NULL;
3223                 prog->knownstrings_flags[num] = 0;
3224                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3225         }
3226         else
3227                 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3228 }
3229
3230 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3231 {
3232         int i, j;
3233
3234         for (i = 0;i < prog->numglobaldefs;i++)
3235         {
3236                 ddef_t *d = &prog->globaldefs[i];
3237                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3238                         continue;
3239                 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3240                         return true;
3241         }
3242
3243         for(j = 0; j < prog->num_edicts; ++j)
3244         {
3245                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3246                 if (ed->priv.required->free)
3247                         continue;
3248                 for (i=0; i<prog->numfielddefs; ++i)
3249                 {
3250                         ddef_t *d = &prog->fielddefs[i];
3251                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3252                                 continue;
3253                         if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3254                                 return true;
3255                 }
3256         }
3257
3258         return false;
3259 }
3260
3261 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3262 {
3263         char vabuf[1024];
3264         char vabuf2[1024];
3265         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3266                 return true; // world or clients
3267         if (edict->priv.required->freetime <= prog->inittime)
3268                 return true; // created during startup
3269         if (prog == SVVM_prog)
3270         {
3271                 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3272                         return true;
3273                 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3274                         return true;
3275                 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3276                         return true;
3277                 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3278                         if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3279                                 return true;
3280                 if(PRVM_serveredictfloat(edict, takedamage))
3281                         return true;
3282                 if(*prvm_leaktest_ignore_classnames.string)
3283                 {
3284                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3285                                 return true;
3286                 }
3287         }
3288         else if (prog == CLVM_prog)
3289         {
3290                 // TODO someone add more stuff here
3291                 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3292                         return true;
3293                 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3294                         return true;
3295                 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3296                         return true;
3297                 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3298                         if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3299                                 return true;
3300                 if(*prvm_leaktest_ignore_classnames.string)
3301                 {
3302                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3303                                 return true;
3304                 }
3305         }
3306         else
3307         {
3308                 // menu prog does not have classnames
3309         }
3310         return false;
3311 }
3312
3313 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3314 {
3315         int i, j;
3316         int edictnum = PRVM_NUM_FOR_EDICT(edict);
3317         const char *targetname = NULL;
3318
3319         if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3320                 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3321
3322         if(targetname)
3323                 if(!*targetname) // ""
3324                         targetname = NULL;
3325
3326         for(j = 0; j < prog->num_edicts; ++j)
3327         {
3328                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3329                 if (ed->priv.required->mark < mark)
3330                         continue;
3331                 if(ed == edict)
3332                         continue;
3333                 if(targetname)
3334                 {
3335                         const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3336                         if(target)
3337                                 if(!strcmp(target, targetname))
3338                                         return true;
3339                 }
3340                 for (i=0; i<prog->numfielddefs; ++i)
3341                 {
3342                         ddef_t *d = &prog->fielddefs[i];
3343                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3344                                 continue;
3345                         if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3346                                 return true;
3347                 }
3348         }
3349
3350         return false;
3351 }
3352
3353 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3354 {
3355         int i, j;
3356         qboolean found_new;
3357         int stage;
3358
3359         // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3360         stage = 1;
3361         for(j = 0; j < prog->num_edicts; ++j)
3362         {
3363                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3364                 if(ed->priv.required->free)
3365                         continue;
3366                 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3367         }
3368         for (i = 0;i < prog->numglobaldefs;i++)
3369         {
3370                 ddef_t *d = &prog->globaldefs[i];
3371                 prvm_edict_t *ed;
3372                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3373                         continue;
3374                 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3375                 if (i < 0 || j >= prog->max_edicts) {
3376                         Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3377                         continue;
3378                 }
3379                 ed = PRVM_EDICT_NUM(j);;
3380                 ed->priv.required->mark = stage;
3381         }
3382
3383         // Future stages: all entities that are referenced by an entity of the previous stage.
3384         do
3385         {
3386                 found_new = false;
3387                 for(j = 0; j < prog->num_edicts; ++j)
3388                 {
3389                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3390                         if(ed->priv.required->free)
3391                                 continue;
3392                         if(ed->priv.required->mark)
3393                                 continue;
3394                         if(PRVM_IsEdictReferenced(prog, ed, stage))
3395                         {
3396                                 ed->priv.required->mark = stage + 1;
3397                                 found_new = true;
3398                         }
3399                 }
3400                 ++stage;
3401         }
3402         while(found_new);
3403         Con_DPrintf("leak check used %d stages to find all references\n", stage);
3404 }
3405
3406 void PRVM_LeakTest(prvm_prog_t *prog)
3407 {
3408         int i, j;
3409         qboolean leaked = false;
3410
3411         if(!prog->leaktest_active)
3412                 return;
3413
3414         // 1. Strings
3415         for (i = 0; i < prog->numknownstrings; ++i)
3416         {
3417                 if(prog->knownstrings[i])
3418                 if(prog->knownstrings_flags[i])
3419                 if(prog->knownstrings_origin[i])
3420                 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3421                 {
3422                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3423                         leaked = true;
3424                 }
3425         }
3426
3427         // 2. Edicts
3428         PRVM_MarkReferencedEdicts(prog);
3429         for(j = 0; j < prog->num_edicts; ++j)
3430         {
3431                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3432                 if(ed->priv.required->free)
3433                         continue;
3434                 if(!ed->priv.required->mark)
3435                 if(ed->priv.required->allocation_origin)
3436                 {
3437                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3438                         PRVM_ED_Print(prog, ed, NULL);
3439                         Con_Print("\n");
3440                         leaked = true;
3441                 }
3442
3443                 ed->priv.required->mark = 0; // clear marks again when done
3444         }
3445
3446         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3447         {
3448                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3449                 if(stringbuffer)
3450                 if(stringbuffer->origin)
3451                 {
3452                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3453                         leaked = true;
3454                 }
3455         }
3456
3457         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3458         {
3459                 if(prog->openfiles[i])
3460                 if(prog->openfiles_origin[i])
3461                 {
3462                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3463                         leaked = true;
3464                 }
3465         }
3466
3467         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3468         {
3469                 if(prog->opensearches[i])
3470                 if(prog->opensearches_origin[i])
3471                 {
3472                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3473                         leaked = true;
3474                 }
3475         }
3476
3477         if(!leaked)
3478                 Con_Printf("Congratulations. No leaks found.\n");
3479 }
3480
3481 void PRVM_GarbageCollection(prvm_prog_t *prog)
3482 {
3483         int limit = prvm_garbagecollection_scan_limit.integer;
3484         prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3485         if (!prvm_garbagecollection_enable.integer)
3486                 return;
3487         // philosophy:
3488         // we like to limit how much scanning we do so it doesn't put a significant
3489         // burden on the cpu, so each of these are not complete scans, we also like
3490         // to have consistent cpu usage so we do a bit of work on each category of
3491         // leaked object every frame
3492         switch (gc->stage)
3493         {
3494         case PRVM_GC_START:
3495                 gc->stage++;
3496                 break;
3497         case PRVM_GC_GLOBALS_MARK:
3498                 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3499                 {
3500                         ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3501                         switch (d->type)
3502                         {
3503                         case ev_string:
3504                                 {
3505                                         prvm_int_t s = prog->globals.ip[d->ofs];
3506                                         if (s & PRVM_KNOWNSTRINGBASE)
3507                                         {
3508                                                 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3509                                                 if (!prog->knownstrings[num])
3510                                                 {
3511                                                         // invalid
3512                                                         Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3513                                                         prog->globals.ip[d->ofs] = 0;
3514                                                         continue;
3515                                                 }
3516                                                 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3517                                         }
3518                                 }
3519                                 break;
3520                         default:
3521                                 break;
3522                         }
3523                 }
3524                 if (gc->globals_mark_progress >= prog->numglobaldefs)
3525                         gc->stage++;
3526                 break;
3527         case PRVM_GC_FIELDS_MARK:
3528                 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3529                 {
3530                         ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3531                         switch (d->type)
3532                         {
3533                         case ev_string:
3534                                 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3535                                 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3536                                 {
3537                                         int entityindex = gc->fields_mark_progress_entity;
3538                                         prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3539                                         if (s & PRVM_KNOWNSTRINGBASE)
3540                                         {
3541                                                 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3542                                                 if (!prog->knownstrings[num])
3543                                                 {
3544                                                         // invalid
3545                                                         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));
3546                                                         prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3547                                                         continue;
3548                                                 }
3549                                                 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3550                                         }
3551                                 }
3552                                 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3553                                 {
3554                                         gc->fields_mark_progress_entity = 0;
3555                                         gc->fields_mark_progress++;
3556                                 }
3557                                 break;
3558                         default:
3559                                 gc->fields_mark_progress_entity = 0;
3560                                 gc->fields_mark_progress++;
3561                                 break;
3562                         }
3563                 }
3564                 if (gc->fields_mark_progress >= prog->numfielddefs)
3565                         gc->stage++;
3566                 break;
3567         case PRVM_GC_KNOWNSTRINGS_SWEEP:
3568                 // free any strzone'd strings that are not marked
3569                 if (!prvm_garbagecollection_strings.integer)
3570                 {
3571                         gc->stage++;
3572                         break;
3573                 }
3574                 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3575                 {
3576                         int num = gc->knownstrings_sweep_progress;
3577                         if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3578                         {
3579                                 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3580                                 {
3581                                         // string has been marked for pruning two passes in a row
3582                                         if (prvm_garbagecollection_notify.integer)
3583                                                 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3584                                         Mem_Free((char *)prog->knownstrings[num]);
3585                                         prog->knownstrings[num] = NULL;
3586                                         prog->knownstrings_flags[num] = 0;
3587                                         prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3588                                 }
3589                                 else
3590                                 {
3591                                         // mark it for pruning next pass
3592                                         prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3593                                 }
3594                         }
3595                 }
3596                 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3597                         gc->stage++;
3598                 break;
3599         case PRVM_GC_RESET:
3600         default:
3601                 memset(gc, 0, sizeof(*gc));
3602         }
3603 }