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