]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
ent_create: Fix potential memory leaks. Misc improvements
[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 void PRVM_ED_CallPrespawnFunction(prvm_prog_t *prog, prvm_edict_t *ent)
1362 {
1363         if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1364         {
1365                 // self = ent
1366                 PRVM_serverglobalfloat(time) = sv.time;
1367                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1368                 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1369         }
1370 }
1371
1372 qboolean PRVM_ED_CallSpawnFunction(prvm_prog_t *prog, prvm_edict_t *ent, const char *data, const char *start)
1373 {
1374         const char *funcname;
1375         mfunction_t *func;
1376         prvm_eval_t *fulldata = NULL;
1377         char vabuf[1024];
1378
1379 //
1380 // immediately call spawn function, but only if there is a self global and a classname
1381 //
1382         if (!ent->priv.required->free)
1383         {
1384                 if (!PRVM_alledictstring(ent, classname))
1385                 {
1386                         Con_Print("No classname for:\n");
1387                         PRVM_ED_Print(prog, ent, NULL);
1388                         PRVM_ED_Free (prog, ent);
1389                         return false;
1390                 }
1391                 /*
1392                  * This is required for FTE compatibility (FreeCS).
1393                  * It copies the key/value pairs themselves into a
1394                  * global for QC to parse on its own.
1395                  */
1396                 else if (data && start)
1397                 {
1398                         if((fulldata = PRVM_ED_FindGlobalEval(prog, "__fullspawndata")))
1399                         {
1400                                 const char *in;
1401                                 char *spawndata;
1402                                 fulldata->string = PRVM_AllocString(prog, data - start + 1, &spawndata);
1403                                 for(in = start; in < data; )
1404                                 {
1405                                         char c = *in++;
1406                                         if(c == '\n')
1407                                                 *spawndata++ = '\t';
1408                                         else
1409                                                 *spawndata++ = c;
1410                                 }
1411                                 *spawndata = 0;
1412                         }
1413                 }
1414
1415                 // look for the spawn function
1416                 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1417                 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1418                 if(!func)
1419                         if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1420                                 func = PRVM_ED_FindFunction (prog, funcname);
1421
1422                 if (!func)
1423                 {
1424                         // check for OnEntityNoSpawnFunction
1425                         if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1426                         {
1427                                 // self = ent
1428                                 PRVM_serverglobalfloat(time) = sv.time;
1429                                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1430                                 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1431                         }
1432                         else
1433                         {
1434                                 
1435                                 Con_DPrint("No spawn function for:\n");
1436                                 if (developer.integer > 0) // don't confuse non-developers with errors  
1437                                         PRVM_ED_Print(prog, ent, NULL);
1438
1439                                 PRVM_ED_Free (prog, ent);
1440                                 return false; // not included in "inhibited" count
1441                         }
1442                 }
1443                 else
1444                 {
1445                         // self = ent
1446                         PRVM_serverglobalfloat(time) = sv.time;
1447                         PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1448                         prog->ExecuteProgram(prog, func - prog->functions, "");
1449                 }
1450                 return true;
1451         }
1452         PRVM_ED_Free(prog, ent);
1453         return false;
1454 }
1455
1456 void PRVM_ED_CallPostspawnFunction (prvm_prog_t *prog, prvm_edict_t *ent)
1457 {
1458         if(!ent->priv.required->free)
1459         if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1460         {
1461                 // self = ent
1462                 PRVM_serverglobalfloat(time) = sv.time;
1463                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1464                 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1465         }
1466 }
1467
1468 /*
1469 ================
1470 PRVM_ED_LoadFromFile
1471
1472 The entities are directly placed in the array, rather than allocated with
1473 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1474 number references out of order.
1475
1476 Creates a server's entity / program execution context by
1477 parsing textual entity definitions out of an ent file.
1478
1479 Used for both fresh maps and savegame loads.  A fresh map would also need
1480 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1481 ================
1482 */
1483 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1484 {
1485         prvm_edict_t *ent;
1486         const char *start;
1487         int parsed, inhibited, spawned, died;
1488
1489         parsed = 0;
1490         inhibited = 0;
1491         spawned = 0;
1492         died = 0;
1493
1494         prvm_reuseedicts_always_allow = host.realtime;
1495
1496         // parse ents
1497         while (1)
1498         {
1499                 start = data;
1500
1501                 // parse the opening brace
1502                 if (!COM_ParseToken_Simple(&data, false, false, true))
1503                         break;
1504                 if (com_token[0] != '{')
1505                         prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1506
1507                 // CHANGED: this is not conform to PR_LoadFromFile
1508                 if(prog->loadintoworld)
1509                 {
1510                         prog->loadintoworld = false;
1511                         ent = PRVM_EDICT_NUM(0);
1512                 }
1513                 else
1514                         ent = PRVM_ED_Alloc(prog);
1515
1516                 // clear it
1517                 if (ent != prog->edicts)        // hack
1518                         memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1519
1520                 data = PRVM_ED_ParseEdict (prog, data, ent);
1521                 parsed++;
1522
1523                 // remove the entity ?
1524                 if(!prog->load_edict(prog, ent))
1525                 {
1526                         PRVM_ED_Free(prog, ent);
1527                         inhibited++;
1528                         continue;
1529                 }
1530
1531                 PRVM_ED_CallPrespawnFunction(prog, ent);
1532
1533                 if(ent->priv.required->free)
1534                 {
1535                         inhibited++;
1536                         continue;
1537                 }
1538
1539                 if(!PRVM_ED_CallSpawnFunction(prog, ent, data, start))
1540                         continue;
1541                 
1542                 PRVM_ED_CallPostspawnFunction(prog, ent);
1543
1544                 spawned++;
1545                 if (ent->priv.required->free)
1546                         died++;
1547         }
1548
1549         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);
1550
1551         prvm_reuseedicts_always_allow = 0;
1552 }
1553
1554 static void PRVM_FindOffsets(prvm_prog_t *prog)
1555 {
1556         // field and global searches use -1 for NULL
1557         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1558         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1559         // function searches use 0 for NULL
1560         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1561 #define PRVM_DECLARE_serverglobalfloat(x)
1562 #define PRVM_DECLARE_serverglobalvector(x)
1563 #define PRVM_DECLARE_serverglobalstring(x)
1564 #define PRVM_DECLARE_serverglobaledict(x)
1565 #define PRVM_DECLARE_serverglobalfunction(x)
1566 #define PRVM_DECLARE_clientglobalfloat(x)
1567 #define PRVM_DECLARE_clientglobalvector(x)
1568 #define PRVM_DECLARE_clientglobalstring(x)
1569 #define PRVM_DECLARE_clientglobaledict(x)
1570 #define PRVM_DECLARE_clientglobalfunction(x)
1571 #define PRVM_DECLARE_menuglobalfloat(x)
1572 #define PRVM_DECLARE_menuglobalvector(x)
1573 #define PRVM_DECLARE_menuglobalstring(x)
1574 #define PRVM_DECLARE_menuglobaledict(x)
1575 #define PRVM_DECLARE_menuglobalfunction(x)
1576 #define PRVM_DECLARE_serverfieldfloat(x)
1577 #define PRVM_DECLARE_serverfieldvector(x)
1578 #define PRVM_DECLARE_serverfieldstring(x)
1579 #define PRVM_DECLARE_serverfieldedict(x)
1580 #define PRVM_DECLARE_serverfieldfunction(x)
1581 #define PRVM_DECLARE_clientfieldfloat(x)
1582 #define PRVM_DECLARE_clientfieldvector(x)
1583 #define PRVM_DECLARE_clientfieldstring(x)
1584 #define PRVM_DECLARE_clientfieldedict(x)
1585 #define PRVM_DECLARE_clientfieldfunction(x)
1586 #define PRVM_DECLARE_menufieldfloat(x)
1587 #define PRVM_DECLARE_menufieldvector(x)
1588 #define PRVM_DECLARE_menufieldstring(x)
1589 #define PRVM_DECLARE_menufieldedict(x)
1590 #define PRVM_DECLARE_menufieldfunction(x)
1591 #define PRVM_DECLARE_serverfunction(x)
1592 #define PRVM_DECLARE_clientfunction(x)
1593 #define PRVM_DECLARE_menufunction(x)
1594 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1595 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1596 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1597 #include "prvm_offsets.h"
1598 #undef PRVM_DECLARE_serverglobalfloat
1599 #undef PRVM_DECLARE_serverglobalvector
1600 #undef PRVM_DECLARE_serverglobalstring
1601 #undef PRVM_DECLARE_serverglobaledict
1602 #undef PRVM_DECLARE_serverglobalfunction
1603 #undef PRVM_DECLARE_clientglobalfloat
1604 #undef PRVM_DECLARE_clientglobalvector
1605 #undef PRVM_DECLARE_clientglobalstring
1606 #undef PRVM_DECLARE_clientglobaledict
1607 #undef PRVM_DECLARE_clientglobalfunction
1608 #undef PRVM_DECLARE_menuglobalfloat
1609 #undef PRVM_DECLARE_menuglobalvector
1610 #undef PRVM_DECLARE_menuglobalstring
1611 #undef PRVM_DECLARE_menuglobaledict
1612 #undef PRVM_DECLARE_menuglobalfunction
1613 #undef PRVM_DECLARE_serverfieldfloat
1614 #undef PRVM_DECLARE_serverfieldvector
1615 #undef PRVM_DECLARE_serverfieldstring
1616 #undef PRVM_DECLARE_serverfieldedict
1617 #undef PRVM_DECLARE_serverfieldfunction
1618 #undef PRVM_DECLARE_clientfieldfloat
1619 #undef PRVM_DECLARE_clientfieldvector
1620 #undef PRVM_DECLARE_clientfieldstring
1621 #undef PRVM_DECLARE_clientfieldedict
1622 #undef PRVM_DECLARE_clientfieldfunction
1623 #undef PRVM_DECLARE_menufieldfloat
1624 #undef PRVM_DECLARE_menufieldvector
1625 #undef PRVM_DECLARE_menufieldstring
1626 #undef PRVM_DECLARE_menufieldedict
1627 #undef PRVM_DECLARE_menufieldfunction
1628 #undef PRVM_DECLARE_serverfunction
1629 #undef PRVM_DECLARE_clientfunction
1630 #undef PRVM_DECLARE_menufunction
1631 #undef PRVM_DECLARE_field
1632 #undef PRVM_DECLARE_global
1633 #undef PRVM_DECLARE_function
1634 }
1635
1636 // not used
1637 /*
1638 typedef struct dpfield_s
1639 {
1640         int type;
1641         char *string;
1642 }
1643 dpfield_t;
1644
1645 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1646
1647 dpfield_t dpfields[] =
1648 {
1649 };
1650 */
1651
1652 /*
1653 ===============
1654 PRVM_ResetProg
1655 ===============
1656 */
1657
1658 #define PO_HASHSIZE 16384
1659 typedef struct po_string_s
1660 {
1661         char *key, *value;
1662         struct po_string_s *nextonhashchain;
1663 }
1664 po_string_t;
1665 typedef struct po_s
1666 {
1667         po_string_t *hashtable[PO_HASHSIZE];
1668 }
1669 po_t;
1670 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1671 {
1672         for(;;)
1673         {
1674                 switch(*in)
1675                 {
1676                         case 0:
1677                                 *out++ = 0;
1678                                 return;
1679                         case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1680                         case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1681                         case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1682                         case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1683                         case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1684                         case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1685                         case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1686                         default:
1687                                 if(*in >= 0 && *in <= 0x1F)
1688                                 {
1689                                         if(outsize >= 4)
1690                                         {
1691                                                 *out++ = '\\';
1692                                                 *out++ = '0' + ((*in & 0700) >> 6);
1693                                                 *out++ = '0' + ((*in & 0070) >> 3);
1694                                                 *out++ = '0' +  (*in & 0007)      ;
1695                                                 outsize -= 4;
1696                                         }
1697                                 }
1698                                 else
1699                                 {
1700                                         if(outsize >= 1)
1701                                         {
1702                                                 *out++ = *in;
1703                                                 outsize -= 1;
1704                                         }
1705                                 }
1706                                 break;
1707                 }
1708                 ++in;
1709         }
1710 }
1711 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1712 {
1713         for(;;)
1714         {
1715                 switch(*in)
1716                 {
1717                         case 0:
1718                                 *out++ = 0;
1719                                 return;
1720                         case '\\':
1721                                 ++in;
1722                                 switch(*in)
1723                                 {
1724                                         case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1725                                         case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1726                                         case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1727                                         case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1728                                         case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1729                                         case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1730                                         case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1731                                         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1732                                                 if(outsize > 0) 
1733                                                         *out = *in - '0';
1734                                                 ++in;
1735                                                 if(*in >= '0' && *in <= '7')
1736                                                 {
1737                                                         if(outsize > 0)
1738                                                                 *out = (*out << 3) | (*in - '0');
1739                                                         ++in;
1740                                                 }
1741                                                 if(*in >= '0' && *in <= '7')
1742                                                 {
1743                                                         if(outsize > 0)
1744                                                                 *out = (*out << 3) | (*in - '0');
1745                                                         ++in;
1746                                                 }
1747                                                 --in;
1748                                                 if(outsize > 0)
1749                                                 {
1750                                                         ++out;
1751                                                         --outsize;
1752                                                 }
1753                                                 break;
1754                                         default:
1755                                                 if(outsize > 0) { *out++ = *in; --outsize; }
1756                                                 break;
1757                                 }
1758                                 break;
1759                         default:
1760                                 if(outsize > 0)
1761                                 {
1762                                         *out++ = *in;
1763                                         --outsize;
1764                                 }
1765                                 break;
1766                 }
1767                 ++in;
1768         }
1769 }
1770 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1771 {
1772         po_t *po = NULL;
1773         const char *p, *q;
1774         int mode;
1775         char inbuf[MAX_INPUTLINE];
1776         char decodedbuf[MAX_INPUTLINE];
1777         size_t decodedpos;
1778         int hashindex;
1779         po_string_t thisstr;
1780         int i;
1781
1782         for (i = 0; i < 2; ++i)
1783         {
1784                 const char *buf = (const char *)
1785                         FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1786                 // first read filename2, then read filename
1787                 // so that progs.dat.de.po wins over common.de.po
1788                 // and within file, last item wins
1789
1790                 if(!buf)
1791                         continue;
1792
1793                 if (!po)
1794                 {
1795                         po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1796                         memset(po, 0, sizeof(*po));
1797                 }
1798
1799                 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1800
1801                 p = buf;
1802                 while(*p)
1803                 {
1804                         if(*p == '#')
1805                         {
1806                                 // skip to newline
1807                                 p = strchr(p, '\n');
1808                                 if(!p)
1809                                         break;
1810                                 ++p;
1811                                 continue;
1812                         }
1813                         if(*p == '\r' || *p == '\n')
1814                         {
1815                                 ++p;
1816                                 continue;
1817                         }
1818                         if(!strncmp(p, "msgid \"", 7))
1819                         {
1820                                 mode = 0;
1821                                 p += 6;
1822                         }
1823                         else if(!strncmp(p, "msgstr \"", 8))
1824                         {
1825                                 mode = 1;
1826                                 p += 7;
1827                         }
1828                         else
1829                         {
1830                                 p = strchr(p, '\n');
1831                                 if(!p)
1832                                         break;
1833                                 ++p;
1834                                 continue;
1835                         }
1836                         decodedpos = 0;
1837                         while(*p == '"')
1838                         {
1839                                 ++p;
1840                                 q = strchr(p, '\n');
1841                                 if(!q)
1842                                         break;
1843                                 if(*(q-1) == '\r')
1844                                         --q;
1845                                 if(*(q-1) != '"')
1846                                         break;
1847                                 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1848                                         break;
1849                                 strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
1850                                 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1851                                 decodedpos += strlen(decodedbuf + decodedpos);
1852                                 if(*q == '\r')
1853                                         ++q;
1854                                 if(*q == '\n')
1855                                         ++q;
1856                                 p = q;
1857                         }
1858                         if(mode == 0)
1859                         {
1860                                 if(thisstr.key)
1861                                         Mem_Free(thisstr.key);
1862                                 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1863                                 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1864                         }
1865                         else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1866                         {
1867                                 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1868                                 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1869                                 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1870                                 thisstr.nextonhashchain = po->hashtable[hashindex];
1871                                 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1872                                 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1873                                 memset(&thisstr, 0, sizeof(thisstr));
1874                         }
1875                 }
1876                 
1877                 Mem_Free((char *) buf);
1878         }
1879
1880         return po;
1881 }
1882 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1883 {
1884         int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1885         po_string_t *p = po->hashtable[hashindex];
1886         while(p)
1887         {
1888                 if(!strcmp(str, p->key))
1889                         return p->value;
1890                 p = p->nextonhashchain;
1891         }
1892         return NULL;
1893 }
1894 static void PRVM_PO_Destroy(po_t *po)
1895 {
1896         int i;
1897         for(i = 0; i < PO_HASHSIZE; ++i)
1898         {
1899                 po_string_t *p = po->hashtable[i];
1900                 while(p)
1901                 {
1902                         po_string_t *q = p;
1903                         p = p->nextonhashchain;
1904                         Mem_Free(q->key);
1905                         Mem_Free(q->value);
1906                         Mem_Free(q);
1907                 }
1908         }
1909         Mem_Free(po);
1910 }
1911
1912 void PRVM_LeakTest(prvm_prog_t *prog);
1913 void PRVM_Prog_Reset(prvm_prog_t *prog)
1914 {
1915         if (prog->loaded)
1916         {
1917                 if(prog->tempstringsbuf.cursize)
1918                         Mem_Free(prog->tempstringsbuf.data);
1919                 prog->tempstringsbuf.cursize = 0;
1920                 PRVM_LeakTest(prog);
1921                 prog->reset_cmd(prog);
1922                 Mem_FreePool(&prog->progs_mempool);
1923                 if(prog->po)
1924                         PRVM_PO_Destroy((po_t *) prog->po);
1925         }
1926         memset(prog,0,sizeof(prvm_prog_t));
1927         prog->break_statement = -1;
1928         prog->watch_global_type = ev_void;
1929         prog->watch_field_type = ev_void;
1930 }
1931
1932 /*
1933 ===============
1934 PRVM_LoadLNO
1935 ===============
1936 */
1937 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1938         fs_offset_t filesize;
1939         unsigned char *lno;
1940         unsigned int *header;
1941         char filename[512];
1942
1943         FS_StripExtension( progname, filename, sizeof( filename ) );
1944         strlcat( filename, ".lno", sizeof( filename ) );
1945
1946         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1947         if( !lno ) {
1948                 return;
1949         }
1950
1951 /*
1952 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
1953 <Spike>    SafeWrite (h, &version, sizeof(int));
1954 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
1955 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
1956 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
1957 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
1958 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1959 */
1960         if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1961         {
1962                 Mem_Free(lno);
1963                 return;
1964         }
1965
1966         header = (unsigned int *) lno;
1967         if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1968                 LittleLong( header[ 1 ] ) == 1 &&
1969                 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs &&
1970                 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals &&
1971                 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs &&
1972                 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
1973         {
1974                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1975                 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1976
1977                 /* gmqcc suports columnums */
1978                 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1979                 {
1980                         prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1981                         memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1982                 }
1983         }
1984         Mem_Free( lno );
1985 }
1986
1987 /*
1988 ===============
1989 PRVM_LoadProgs
1990 ===============
1991 */
1992 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
1993 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)
1994 {
1995         int i;
1996         dprograms_t *dprograms;
1997         dstatement_t *instatements;
1998         ddef_t *infielddefs;
1999         ddef_t *inglobaldefs;
2000         int *inglobals;
2001         dfunction_t *infunctions;
2002         char *instrings;
2003         fs_offset_t filesize;
2004         int requiredglobalspace;
2005         opcode_t op;
2006         int a;
2007         int b;
2008         int c;
2009         union
2010         {
2011                 unsigned int i;
2012                 float f;
2013         }
2014         u;
2015         unsigned int d;
2016         char vabuf[1024];
2017         char vabuf2[1024];
2018         cvar_t *cvar;
2019
2020         if (prog->loaded)
2021                 prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
2022
2023         Host_LockSession(); // all progs can use the session cvar
2024         Crypto_LoadKeys(); // all progs might use the keys at init time
2025
2026         if (data)
2027         {
2028                 dprograms = (dprograms_t *) data;
2029                 filesize = size;
2030         }
2031         else
2032                 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2033         if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2034                 prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name);
2035         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2036
2037         prog->profiletime = Sys_DirtyTime();
2038         prog->starttime = host.realtime;
2039
2040         requiredglobalspace = 0;
2041         for (i = 0;i < numrequiredglobals;i++)
2042                 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2043
2044         prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2045
2046 // byte swap the header
2047         prog->progs_version = LittleLong(dprograms->version);
2048         prog->progs_crc = LittleLong(dprograms->crc);
2049         if (prog->progs_version != PROG_VERSION)
2050                 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
2051         instatements = (dstatement_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2052         prog->progs_numstatements = LittleLong(dprograms->numstatements);
2053         inglobaldefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2054         prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2055         infielddefs = (ddef_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2056         prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2057         infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2058         prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2059         instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2060         prog->progs_numstrings = LittleLong(dprograms->numstrings);
2061         inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2062         prog->progs_numglobals = LittleLong(dprograms->numglobals);
2063         prog->progs_entityfields = LittleLong(dprograms->entityfields);
2064
2065         prog->numstatements = prog->progs_numstatements;
2066         prog->numglobaldefs = prog->progs_numglobaldefs;
2067         prog->numfielddefs = prog->progs_numfielddefs;
2068         prog->numfunctions = prog->progs_numfunctions;
2069         prog->numstrings = prog->progs_numstrings;
2070         prog->numglobals = prog->progs_numglobals;
2071         prog->entityfields = prog->progs_entityfields;
2072
2073         if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2074                 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2075         prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2076         memcpy(prog->strings, instrings, prog->progs_numstrings);
2077         prog->stringssize = prog->progs_numstrings;
2078
2079         prog->numknownstrings = 0;
2080         prog->maxknownstrings = 0;
2081         prog->knownstrings = NULL;
2082         prog->knownstrings_flags = NULL;
2083
2084         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2085
2086         // we need to expand the globaldefs and fielddefs to include engine defs
2087         prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t));
2088         prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2089                 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2090                 // when trying to return the last or second-last global
2091                 // (RETURN always returns a vector, there is no RETURN_F instruction)
2092         prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t));
2093         // we need to convert the statements to our memory format
2094         prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2095         // allocate space for profiling statement usage
2096         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2097         prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2098         // functions need to be converted to the memory format
2099         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2100
2101         for (i = 0;i < prog->progs_numfunctions;i++)
2102         {
2103                 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2104                 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2105                 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2106                 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2107                 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2108                 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2109                 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2110                 if(prog->functions[i].first_statement >= prog->numstatements)
2111                         prog->error_cmd("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, prog->name);
2112                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2113         }
2114
2115         // copy the globaldefs to the new globaldefs list
2116         for (i=0 ; i<prog->numglobaldefs ; i++)
2117         {
2118                 prog->globaldefs[i].type = LittleShort(inglobaldefs[i].type);
2119                 prog->globaldefs[i].ofs = LittleShort(inglobaldefs[i].ofs);
2120                 prog->globaldefs[i].s_name = LittleLong(inglobaldefs[i].s_name);
2121                 // TODO bounds check ofs, s_name
2122         }
2123
2124         // append the required globals
2125         for (i = 0;i < numrequiredglobals;i++)
2126         {
2127                 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2128                 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2129                 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2130                 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2131                         prog->numglobals += 3;
2132                 else
2133                         prog->numglobals++;
2134                 prog->numglobaldefs++;
2135         }
2136
2137         // copy the progs fields to the new fields list
2138         for (i = 0;i < prog->numfielddefs;i++)
2139         {
2140                 prog->fielddefs[i].type = LittleShort(infielddefs[i].type);
2141                 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2142                         prog->error_cmd("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", prog->name);
2143                 prog->fielddefs[i].ofs = LittleShort(infielddefs[i].ofs);
2144                 prog->fielddefs[i].s_name = LittleLong(infielddefs[i].s_name);
2145                 // TODO bounds check ofs, s_name
2146         }
2147
2148         // append the required fields
2149         for (i = 0;i < numrequiredfields;i++)
2150         {
2151                 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2152                 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2153                 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2154                 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2155                         prog->entityfields += 3;
2156                 else
2157                         prog->entityfields++;
2158                 prog->numfielddefs++;
2159         }
2160
2161         // LadyHavoc: TODO: reorder globals to match engine struct
2162         // LadyHavoc: TODO: reorder fields to match engine struct
2163 #define remapglobal(index) (index)
2164 #define remapfield(index) (index)
2165
2166         // copy globals
2167         // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2168         for (i = 0;i < prog->progs_numglobals;i++)
2169         {
2170                 u.i = LittleLong(inglobals[i]);
2171                 // most globals are 0, we only need to deal with the ones that are not
2172                 if (u.i)
2173                 {
2174                         d = u.i & 0xFF800000;
2175                         if ((d == 0xFF800000) || (d == 0))
2176                         {
2177                                 // Looks like an integer (expand to int64)
2178                                 prog->globals.ip[remapglobal(i)] = u.i;
2179                         }
2180                         else
2181                         {
2182                                 // Looks like a float (expand to double)
2183                                 prog->globals.fp[remapglobal(i)] = u.f;
2184                         }
2185                 }
2186         }
2187
2188         // LadyHavoc: TODO: support 32bit progs statement formats
2189         // copy, remap globals in statements, bounds check
2190         for (i = 0;i < prog->progs_numstatements;i++)
2191         {
2192                 op = (opcode_t)LittleShort(instatements[i].op);
2193                 a = (unsigned short)LittleShort(instatements[i].a);
2194                 b = (unsigned short)LittleShort(instatements[i].b);
2195                 c = (unsigned short)LittleShort(instatements[i].c);
2196                 switch (op)
2197                 {
2198                 case OP_IF:
2199                 case OP_IFNOT:
2200                         b = (short)b;
2201                         if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2202                                 prog->error_cmd("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, prog->name);
2203                         prog->statements[i].op = op;
2204                         prog->statements[i].operand[0] = remapglobal(a);
2205                         prog->statements[i].operand[1] = -1;
2206                         prog->statements[i].operand[2] = -1;
2207                         prog->statements[i].jumpabsolute = i + b;
2208                         break;
2209                 case OP_GOTO:
2210                         a = (short)a;
2211                         if (a + i < 0 || a + i >= prog->progs_numstatements)
2212                                 prog->error_cmd("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, prog->name);
2213                         prog->statements[i].op = op;
2214                         prog->statements[i].operand[0] = -1;
2215                         prog->statements[i].operand[1] = -1;
2216                         prog->statements[i].operand[2] = -1;
2217                         prog->statements[i].jumpabsolute = i + a;
2218                         break;
2219                 default:
2220                         Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name);
2221                         break;
2222                 // global global global
2223                 case OP_ADD_F:
2224                 case OP_ADD_V:
2225                 case OP_SUB_F:
2226                 case OP_SUB_V:
2227                 case OP_MUL_F:
2228                 case OP_MUL_V:
2229                 case OP_MUL_FV:
2230                 case OP_MUL_VF:
2231                 case OP_DIV_F:
2232                 case OP_BITAND:
2233                 case OP_BITOR:
2234                 case OP_GE:
2235                 case OP_LE:
2236                 case OP_GT:
2237                 case OP_LT:
2238                 case OP_AND:
2239                 case OP_OR:
2240                 case OP_EQ_F:
2241                 case OP_EQ_V:
2242                 case OP_EQ_S:
2243                 case OP_EQ_E:
2244                 case OP_EQ_FNC:
2245                 case OP_NE_F:
2246                 case OP_NE_V:
2247                 case OP_NE_S:
2248                 case OP_NE_E:
2249                 case OP_NE_FNC:
2250                 case OP_ADDRESS:
2251                 case OP_LOAD_F:
2252                 case OP_LOAD_FLD:
2253                 case OP_LOAD_ENT:
2254                 case OP_LOAD_S:
2255                 case OP_LOAD_FNC:
2256                 case OP_LOAD_V:
2257                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2258                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
2259                         prog->statements[i].op = op;
2260                         prog->statements[i].operand[0] = remapglobal(a);
2261                         prog->statements[i].operand[1] = remapglobal(b);
2262                         prog->statements[i].operand[2] = remapglobal(c);
2263                         prog->statements[i].jumpabsolute = -1;
2264                         break;
2265                 // global none global
2266                 case OP_NOT_F:
2267                 case OP_NOT_V:
2268                 case OP_NOT_S:
2269                 case OP_NOT_FNC:
2270                 case OP_NOT_ENT:
2271                         if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2272                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2273                         prog->statements[i].op = op;
2274                         prog->statements[i].operand[0] = remapglobal(a);
2275                         prog->statements[i].operand[1] = -1;
2276                         prog->statements[i].operand[2] = remapglobal(c);
2277                         prog->statements[i].jumpabsolute = -1;
2278                         break;
2279                 // 2 globals
2280                 case OP_STOREP_F:
2281                 case OP_STOREP_ENT:
2282                 case OP_STOREP_FLD:
2283                 case OP_STOREP_S:
2284                 case OP_STOREP_FNC:
2285                 case OP_STORE_F:
2286                 case OP_STORE_ENT:
2287                 case OP_STORE_FLD:
2288                 case OP_STORE_S:
2289                 case OP_STORE_FNC:
2290                 case OP_STATE:
2291                 case OP_STOREP_V:
2292                 case OP_STORE_V:
2293                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2294                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2295                         prog->statements[i].op = op;
2296                         prog->statements[i].operand[0] = remapglobal(a);
2297                         prog->statements[i].operand[1] = remapglobal(b);
2298                         prog->statements[i].operand[2] = -1;
2299                         prog->statements[i].jumpabsolute = -1;
2300                         break;
2301                 // 1 global
2302                 case OP_CALL0:
2303                         if ( a < prog->progs_numglobals)
2304                                 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2305                                         if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2306                                                 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2307                                                         ++prog->numexplicitcoveragestatements;
2308                 case OP_CALL1:
2309                 case OP_CALL2:
2310                 case OP_CALL3:
2311                 case OP_CALL4:
2312                 case OP_CALL5:
2313                 case OP_CALL6:
2314                 case OP_CALL7:
2315                 case OP_CALL8:
2316                 case OP_DONE:
2317                 case OP_RETURN:
2318                         if ( a >= prog->progs_numglobals)
2319                                 prog->error_cmd("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2320                         prog->statements[i].op = op;
2321                         prog->statements[i].operand[0] = remapglobal(a);
2322                         prog->statements[i].operand[1] = -1;
2323                         prog->statements[i].operand[2] = -1;
2324                         prog->statements[i].jumpabsolute = -1;
2325                         break;
2326                 }
2327         }
2328         if(prog->numstatements < 1)
2329         {
2330                 prog->error_cmd("PRVM_LoadProgs: empty program in %s", prog->name);
2331         }
2332         else switch(prog->statements[prog->numstatements - 1].op)
2333         {
2334                 case OP_RETURN:
2335                 case OP_GOTO:
2336                 case OP_DONE:
2337                         break;
2338                 default:
2339                         prog->error_cmd("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", prog->name);
2340                         break;
2341         }
2342
2343         // we're done with the file now
2344         if(!data)
2345                 Mem_Free(dprograms);
2346         dprograms = NULL;
2347
2348         // check required functions
2349         for(i=0 ; i < numrequiredfunc ; i++)
2350                 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2351                         prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2352
2353         PRVM_LoadLNO(prog, filename);
2354
2355         PRVM_Init_Exec(prog);
2356
2357         if(*prvm_language.string)
2358         // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2359         // later idea: include a list of authorized .po file checksums with the csprogs
2360         {
2361                 qboolean deftrans = prog == CLVM_prog;
2362                 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2363                 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2364                 {
2365                         for (i=0 ; i<prog->numglobaldefs ; i++)
2366                         {
2367                                 const char *name;
2368                                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2369                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2370                                 if(name && !strncmp(name, "dotranslate_", 12))
2371                                 {
2372                                         deftrans = false;
2373                                         break;
2374                                 }
2375                         }
2376                 }
2377                 if(!strcmp(prvm_language.string, "dump"))
2378                 {
2379                         qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2380                         Con_Printf("Dumping to %s.pot\n", realfilename);
2381                         if(f)
2382                         {
2383                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2384                                 {
2385                                         const char *name;
2386                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2387                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2388                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2389                                         {
2390                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2391                                                 const char *value = PRVM_GetString(prog, val->string);
2392                                                 if(*value)
2393                                                 {
2394                                                         char buf[MAX_INPUTLINE];
2395                                                         PRVM_PO_UnparseString(buf, value, sizeof(buf));
2396                                                         FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2397                                                 }
2398                                         }
2399                                 }
2400                                 FS_Close(f);
2401                         }
2402                 }
2403                 else
2404                 {
2405                         po_t *po = PRVM_PO_Load(
2406                                         va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2407                                         va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2408                                         prog->progs_mempool);
2409                         if(po)
2410                         {
2411                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2412                                 {
2413                                         const char *name;
2414                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2415                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2416                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2417                                         {
2418                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2419                                                 const char *value = PRVM_GetString(prog, val->string);
2420                                                 if(*value)
2421                                                 {
2422                                                         value = PRVM_PO_Lookup(po, value);
2423                                                         if(value)
2424                                                                 val->string = PRVM_SetEngineString(prog, value);
2425                                                 }
2426                                         }
2427                                 }
2428                         }
2429                 }
2430         }
2431
2432         for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2433                 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2434
2435         for (i=0 ; i<prog->numglobaldefs ; i++)
2436         {
2437                 const char *name;
2438                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2439                 //Con_Printf("found var %s\n", name);
2440                 if(name
2441                         && !strncmp(name, "autocvar_", 9)
2442                         && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2443                 )
2444                 {
2445                         prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2446                         cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2447                         //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2448                         if(!cvar)
2449                         {
2450                                 const char *value;
2451                                 char buf[128];
2452                                 int prec[3];
2453                                 float f;
2454                                 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2455                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2456                                 {
2457                                         case ev_float:
2458                                                 if((float)((int)(val->_float)) == val->_float)
2459                                                         dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2460                                                 else
2461                                                 {
2462                                                         // ftos_slow
2463                                                         f = val->_float;
2464                                                         for (int precision = 7; precision <= 9; ++precision) {
2465                                                                 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2466                                                                 if ((float)atof(buf) == f) {
2467                                                                         break;
2468                                                                 }
2469                                                         }
2470                                                 }
2471                                                 value = buf;
2472                                                 break;
2473                                         case ev_vector:
2474                                                 for (i = 0; i < 3; ++i)
2475                                                 {
2476                                                         prec[i] = 9;
2477                                                         f = val->vector[i];
2478                                                         for (int precision = 7; precision <= 9; ++precision) {
2479                                                                 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2480                                                                 if ((float)atof(buf) == f) {
2481                                                                         prec[i] = precision;
2482                                                                         break;
2483                                                                 }
2484                                                         }
2485                                                 }
2486                                                 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2487                                                 value = buf;
2488                                                 break;
2489                                         case ev_string:
2490                                                 value = PRVM_GetString(prog, val->string);
2491                                                 break;
2492                                         default:
2493                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2494                                                 goto fail;
2495                                 }
2496                                 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2497                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2498                                 {
2499                                         val->string = PRVM_SetEngineString(prog, cvar->string);
2500                                         cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2501                                 }
2502                                 if(!cvar)
2503                                         prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2504                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2505                         }
2506                         else if((cvar->flags & CVAR_PRIVATE) == 0)
2507                         {
2508                                 // MUST BE SYNCED WITH cvar.c Cvar_Set
2509                                 int j;
2510                                 const char *s;
2511                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2512                                 {
2513                                         case ev_float:
2514                                                 val->_float = cvar->value;
2515                                                 break;
2516                                         case ev_vector:
2517                                                 s = cvar->string;
2518                                                 VectorClear(val->vector);
2519                                                 for (j = 0;j < 3;j++)
2520                                                 {
2521                                                         while (*s && ISWHITESPACE(*s))
2522                                                                 s++;
2523                                                         if (!*s)
2524                                                                 break;
2525                                                         val->vector[j] = atof(s);
2526                                                         while (!ISWHITESPACE(*s))
2527                                                                 s++;
2528                                                         if (!*s)
2529                                                                 break;
2530                                                 }
2531                                                 break;
2532                                         case ev_string:
2533                                                 val->string = PRVM_SetEngineString(prog, cvar->string);
2534                                                 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2535                                                 break;
2536                                         default:
2537                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2538                                                 goto fail;
2539                                 }
2540                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2541                         }
2542                         else
2543                                 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2544                 }
2545 fail:
2546                 ;
2547         }
2548
2549         prog->loaded = true;
2550
2551         PRVM_UpdateBreakpoints(prog);
2552
2553         // set flags & ddef_ts in prog
2554
2555         prog->flag = 0;
2556
2557         PRVM_FindOffsets(prog);
2558
2559         prog->init_cmd(prog);
2560
2561         // init mempools
2562         PRVM_MEM_Alloc(prog);
2563
2564         Con_Printf("%s: program loaded (crc %i, size %iK)\n", prog->name, prog->filecrc, (int)(filesize/1024));
2565
2566         // Inittime is at least the time when this function finished. However,
2567         // later events may bump it.
2568         prog->inittime = host.realtime;
2569 }
2570
2571
2572 static void PRVM_Fields_f(cmd_state_t *cmd)
2573 {
2574         prvm_prog_t *prog;
2575         int i, j, ednum, used, usedamount;
2576         int *counts;
2577         char tempstring[MAX_INPUTLINE], tempstring2[260];
2578         const char *name;
2579         prvm_edict_t *ed;
2580         ddef_t *d;
2581         prvm_eval_t *val;
2582
2583         // TODO
2584         /*
2585         if (!sv.active)
2586         {
2587                 Con_Print("no progs loaded\n");
2588                 return;
2589         }
2590         */
2591
2592         if(Cmd_Argc(cmd) != 2)
2593         {
2594                 Con_Print("prvm_fields <program name>\n");
2595                 return;
2596         }
2597
2598         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2599                 return;
2600
2601         counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2602         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2603         {
2604                 ed = PRVM_EDICT_NUM(ednum);
2605                 if (ed->priv.required->free)
2606                         continue;
2607                 for (i = 1;i < prog->numfielddefs;i++)
2608                 {
2609                         d = &prog->fielddefs[i];
2610                         name = PRVM_GetString(prog, d->s_name);
2611                         if (name[strlen(name)-2] == '_')
2612                                 continue;       // skip _x, _y, _z vars
2613                         val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2614                         // if the value is still all 0, skip the field
2615                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2616                         {
2617                                 if (val->ivector[j])
2618                                 {
2619                                         counts[i]++;
2620                                         break;
2621                                 }
2622                         }
2623                 }
2624         }
2625         used = 0;
2626         usedamount = 0;
2627         tempstring[0] = 0;
2628         for (i = 0;i < prog->numfielddefs;i++)
2629         {
2630                 d = &prog->fielddefs[i];
2631                 name = PRVM_GetString(prog, d->s_name);
2632                 if (name[strlen(name)-2] == '_')
2633                         continue;       // skip _x, _y, _z vars
2634                 switch(d->type & ~DEF_SAVEGLOBAL)
2635                 {
2636                 case ev_string:
2637                         strlcat(tempstring, "string   ", sizeof(tempstring));
2638                         break;
2639                 case ev_entity:
2640                         strlcat(tempstring, "entity   ", sizeof(tempstring));
2641                         break;
2642                 case ev_function:
2643                         strlcat(tempstring, "function ", sizeof(tempstring));
2644                         break;
2645                 case ev_field:
2646                         strlcat(tempstring, "field    ", sizeof(tempstring));
2647                         break;
2648                 case ev_void:
2649                         strlcat(tempstring, "void     ", sizeof(tempstring));
2650                         break;
2651                 case ev_float:
2652                         strlcat(tempstring, "float    ", sizeof(tempstring));
2653                         break;
2654                 case ev_vector:
2655                         strlcat(tempstring, "vector   ", sizeof(tempstring));
2656                         break;
2657                 case ev_pointer:
2658                         strlcat(tempstring, "pointer  ", sizeof(tempstring));
2659                         break;
2660                 default:
2661                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2662                         strlcat(tempstring, tempstring2, sizeof(tempstring));
2663                         break;
2664                 }
2665                 if (strlen(name) > sizeof(tempstring2)-4)
2666                 {
2667                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2668                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2669                         tempstring2[sizeof(tempstring2)-1] = 0;
2670                         name = tempstring2;
2671                 }
2672                 strlcat(tempstring, name, sizeof(tempstring));
2673                 for (j = (int)strlen(name);j < 25;j++)
2674                         strlcat(tempstring, " ", sizeof(tempstring));
2675                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2676                 strlcat(tempstring, tempstring2, sizeof(tempstring));
2677                 strlcat(tempstring, "\n", sizeof(tempstring));
2678                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2679                 {
2680                         Con_Print(tempstring);
2681                         tempstring[0] = 0;
2682                 }
2683                 if (counts[i])
2684                 {
2685                         used++;
2686                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2687                 }
2688         }
2689         Mem_Free(counts);
2690         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);
2691 }
2692
2693 static void PRVM_Globals_f(cmd_state_t *cmd)
2694 {
2695         prvm_prog_t *prog;
2696         int i;
2697         const char *wildcard;
2698         int numculled;
2699                 numculled = 0;
2700         // TODO
2701         /*if (!sv.active)
2702         {
2703                 Con_Print("no progs loaded\n");
2704                 return;
2705         }*/
2706         if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2707         {
2708                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2709                 return;
2710         }
2711
2712         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2713                 return;
2714
2715         if( Cmd_Argc(cmd) == 3)
2716                 wildcard = Cmd_Argv(cmd, 2);
2717         else
2718                 wildcard = NULL;
2719
2720         Con_Printf("%s :", prog->name);
2721
2722         for (i = 0;i < prog->numglobaldefs;i++)
2723         {
2724                 if(wildcard)
2725                         if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2726                         {
2727                                 numculled++;
2728                                 continue;
2729                         }
2730                 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2731         }
2732         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2733 }
2734
2735 /*
2736 ===============
2737 PRVM_Global
2738 ===============
2739 */
2740 static void PRVM_Global_f(cmd_state_t *cmd)
2741 {
2742         prvm_prog_t *prog;
2743         ddef_t *global;
2744         char valuebuf[MAX_INPUTLINE];
2745         if( Cmd_Argc(cmd) != 3 ) {
2746                 Con_Printf( "prvm_global <program name> <global name>\n" );
2747                 return;
2748         }
2749
2750         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2751                 return;
2752
2753         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2754         if( !global )
2755                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2756         else
2757                 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2758 }
2759
2760 /*
2761 ===============
2762 PRVM_GlobalSet
2763 ===============
2764 */
2765 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2766 {
2767         prvm_prog_t *prog;
2768         ddef_t *global;
2769         if( Cmd_Argc(cmd) != 4 ) {
2770                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2771                 return;
2772         }
2773
2774         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2775                 return;
2776
2777         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2778         if( !global )
2779                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2780         else
2781                 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2782 }
2783
2784 /*
2785 ======================
2786 Break- and Watchpoints
2787 ======================
2788 */
2789 typedef struct
2790 {
2791         char break_statement[256];
2792         char watch_global[256];
2793         int watch_edict;
2794         char watch_field[256];
2795 }
2796 debug_data_t;
2797 static debug_data_t debug_data[PRVM_PROG_MAX];
2798
2799 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2800 {
2801         char vabuf[1024];
2802         Con_Printf("PRVM_Breakpoint: %s\n", text);
2803         PRVM_PrintState(prog, stack_index);
2804         if (prvm_breakpointdump.integer)
2805                 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2806 }
2807
2808 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2809 {
2810         size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2811         if (memcmp(o, n, sz))
2812         {
2813                 char buf[1024];
2814                 char valuebuf_o[128];
2815                 char valuebuf_n[128];
2816                 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2817                 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2818                 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2819                 PRVM_Breakpoint(prog, stack_index, buf);
2820                 memcpy(o, n, sz);
2821         }
2822 }
2823
2824 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2825 {
2826         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2827         if (!prog->loaded)
2828                 return;
2829         if (debug->break_statement[0])
2830         {
2831                 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2832                 {
2833                         prog->break_statement = atoi(debug->break_statement);
2834                         prog->break_stack_index = 0;
2835                 }
2836                 else
2837                 {
2838                         mfunction_t *func;
2839                         func = PRVM_ED_FindFunction (prog, debug->break_statement);
2840                         if (!func)
2841                         {
2842                                 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2843                                 prog->break_statement = -1;
2844                         }
2845                         else
2846                         {
2847                                 prog->break_statement = func->first_statement;
2848                                 prog->break_stack_index = 1;
2849                         }
2850                 }
2851                 if (prog->break_statement >= -1)
2852                         Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
2853         }
2854         else
2855                 prog->break_statement = -1;
2856
2857         if (debug->watch_global[0])
2858         {
2859                 ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
2860                 if( !global )
2861                 {
2862                         Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
2863                         prog->watch_global_type = ev_void;
2864                 }
2865                 else
2866                 {
2867                         size_t sz = sizeof(prvm_vec_t) * ((global->type  & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2868                         prog->watch_global = global->ofs;
2869                         prog->watch_global_type = (etype_t)global->type;
2870                         memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
2871                 }
2872                 if (prog->watch_global_type != ev_void)
2873                         Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
2874         }
2875         else
2876                 prog->watch_global_type = ev_void;
2877
2878         if (debug->watch_field[0])
2879         {
2880                 ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
2881                 if( !field )
2882                 {
2883                         Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
2884                         prog->watch_field_type = ev_void;
2885                 }
2886                 else
2887                 {
2888                         size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2889                         prog->watch_edict = debug->watch_edict;
2890                         prog->watch_field = field->ofs;
2891                         prog->watch_field_type = (etype_t)field->type;
2892                         if (prog->watch_edict < prog->num_edicts)
2893                                 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
2894                         else
2895                                 memset(&prog->watch_edictfield_value, 0, sz);
2896                 }
2897                 if (prog->watch_edict != ev_void)
2898                         Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
2899         }
2900         else
2901                 prog->watch_field_type = ev_void;
2902 }
2903
2904 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
2905 {
2906         prvm_prog_t *prog;
2907
2908         if( Cmd_Argc(cmd) == 2 ) {
2909                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2910                         return;
2911                 {
2912                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2913                         debug->break_statement[0] = 0;
2914                 }
2915                 PRVM_UpdateBreakpoints(prog);
2916                 return;
2917         }
2918         if( Cmd_Argc(cmd) != 3 ) {
2919                 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
2920                 return;
2921         }
2922
2923         if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2924                 return;
2925
2926         {
2927                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2928                 strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
2929         }
2930         PRVM_UpdateBreakpoints(prog);
2931 }
2932
2933 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
2934 {
2935         prvm_prog_t *prog;
2936
2937         if( Cmd_Argc(cmd) == 2 ) {
2938                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2939                         return;
2940                 {
2941                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2942                         debug->watch_global[0] = 0;
2943                 }
2944                 PRVM_UpdateBreakpoints(prog);
2945                 return;
2946         }
2947         if( Cmd_Argc(cmd) != 3 ) {
2948                 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
2949                 return;
2950         }
2951
2952         if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2953                 return;
2954
2955         {
2956                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2957                 strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
2958         }
2959         PRVM_UpdateBreakpoints(prog);
2960 }
2961
2962 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
2963 {
2964         prvm_prog_t *prog;
2965
2966         if( Cmd_Argc(cmd) == 2 ) {
2967                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2968                         return;
2969                 {
2970                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2971                         debug->watch_field[0] = 0;
2972                 }
2973                 PRVM_UpdateBreakpoints(prog);
2974                 return;
2975         }
2976         if( Cmd_Argc(cmd) != 4 ) {
2977                 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
2978                 return;
2979         }
2980
2981         if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
2982                 return;
2983
2984         {
2985                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2986                 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
2987                 strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
2988         }
2989         PRVM_UpdateBreakpoints(prog);
2990 }
2991
2992 /*
2993 ===============
2994 PRVM_Init
2995 ===============
2996 */
2997 void PRVM_Init (void)
2998 {
2999         Cmd_AddCommand(CMD_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
3000         Cmd_AddCommand(CMD_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
3001         Cmd_AddCommand(CMD_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
3002         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)");
3003         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");
3004         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)");
3005         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)");
3006         Cmd_AddCommand(CMD_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
3007         Cmd_AddCommand(CMD_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
3008         Cmd_AddCommand(CMD_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
3009         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)");
3010         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");
3011         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");
3012         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)");
3013         Cmd_AddCommand(CMD_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
3014         Cmd_AddCommand(CMD_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
3015         Cmd_AddCommand(CMD_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
3016         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");
3017         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");
3018         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");
3019
3020         Cvar_RegisterVariable (&prvm_language);
3021         Cvar_RegisterVariable (&prvm_traceqc);
3022         Cvar_RegisterVariable (&prvm_statementprofiling);
3023         Cvar_RegisterVariable (&prvm_timeprofiling);
3024         Cvar_RegisterVariable (&prvm_coverage);
3025         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
3026         Cvar_RegisterVariable (&prvm_leaktest);
3027         Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
3028         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
3029         Cvar_RegisterVariable (&prvm_errordump);
3030         Cvar_RegisterVariable (&prvm_breakpointdump);
3031         Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
3032         Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
3033         Cvar_RegisterVariable (&prvm_garbagecollection_enable);
3034         Cvar_RegisterVariable (&prvm_garbagecollection_notify);
3035         Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
3036         Cvar_RegisterVariable (&prvm_garbagecollection_strings);
3037         Cvar_RegisterVariable (&prvm_stringdebug);
3038
3039         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
3040         prvm_runawaycheck = !COM_CheckParm("-norunaway");
3041
3042         //VM_Cmd_Init();
3043 }
3044
3045 /*
3046 ===============
3047 PRVM_InitProg
3048 ===============
3049 */
3050 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
3051 {
3052         PRVM_Prog_Reset(prog);
3053         prog->leaktest_active = prvm_leaktest.integer != 0;
3054         prog->console_cmd = cmd;
3055 }
3056
3057 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
3058 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3059 {
3060         prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3061         return 0;
3062 }
3063
3064 #define PRVM_KNOWNSTRINGBASE 0x40000000
3065
3066 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3067 {
3068         if (num < 0)
3069         {
3070                 // invalid
3071                 if (prvm_stringdebug.integer)
3072                         VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3073                 return "";
3074         }
3075         else if (num < prog->stringssize)
3076         {
3077                 // constant string from progs.dat
3078                 return prog->strings + num;
3079         }
3080         else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3081         {
3082                 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3083                 num -= prog->stringssize;
3084                 if (num < prog->tempstringsbuf.cursize)
3085                         return (char *)prog->tempstringsbuf.data + num;
3086                 else
3087                 {
3088                         if (prvm_stringdebug.integer)
3089                                 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3090                         return "";
3091                 }
3092         }
3093         else if (num & PRVM_KNOWNSTRINGBASE)
3094         {
3095                 // allocated string
3096                 num = num - PRVM_KNOWNSTRINGBASE;
3097                 if (num >= 0 && num < prog->numknownstrings)
3098                 {
3099                         if (!prog->knownstrings[num])
3100                         {
3101                                 if (prvm_stringdebug.integer)
3102                                         VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3103                                 return "";
3104                         }
3105                         // refresh the garbage collection on the string - this guards
3106                         // against a certain sort of repeated migration to earlier
3107                         // points in the scan that could otherwise result in the string
3108                         // being freed for being unused
3109                         prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3110                         return prog->knownstrings[num];
3111                 }
3112                 else
3113                 {
3114                         if (prvm_stringdebug.integer)
3115                                 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3116                         return "";
3117                 }
3118         }
3119         else
3120         {
3121                 // invalid string offset
3122                 if (prvm_stringdebug.integer)
3123                         VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3124                 return "";
3125         }
3126 }
3127
3128 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3129 {
3130         const char *old;
3131         i = i - PRVM_KNOWNSTRINGBASE;
3132         if (i < 0 || i >= prog->numknownstrings)
3133                 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3134         else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3135                 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3136         old = prog->knownstrings[i];
3137         prog->knownstrings[i] = s;
3138         return old;
3139 }
3140
3141 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3142 {
3143         if (i >= prog->numknownstrings)
3144         {
3145                 if (i >= prog->maxknownstrings)
3146                 {
3147                         const char **oldstrings = prog->knownstrings;
3148                         const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3149                         const char **oldstrings_origin = prog->knownstrings_origin;
3150                         prog->maxknownstrings += 128;
3151                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3152                         prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3153                         if (prog->leaktest_active)
3154                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3155                         if (prog->numknownstrings)
3156                         {
3157                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3158                                 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3159                                 if (prog->leaktest_active)
3160                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3161                         }
3162                 }
3163                 prog->numknownstrings++;
3164         }
3165         prog->firstfreeknownstring = i + 1;
3166         prog->knownstrings[i] = s;
3167         // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3168         prog->knownstrings_flags[i] = flags;
3169         if (prog->leaktest_active)
3170                 prog->knownstrings_origin[i] = NULL;
3171 }
3172
3173 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3174 {
3175         int i;
3176         if (!s)
3177                 return 0;
3178         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3179                 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3180         // if it's in the tempstrings area, use a reserved range
3181         // (otherwise we'd get millions of useless string offsets cluttering the database)
3182         if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3183                 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3184         // see if it's a known string address
3185         for (i = 0;i < prog->numknownstrings;i++)
3186                 if (prog->knownstrings[i] == s)
3187                         return PRVM_KNOWNSTRINGBASE + i;
3188         // new unknown engine string
3189         if (developer_insane.integer)
3190                 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3191         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3192                 if (!prog->knownstrings[i])
3193                         break;
3194         PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3195         return PRVM_KNOWNSTRINGBASE + i;
3196 }
3197
3198 // temp string handling
3199
3200 // all tempstrings go into this buffer consecutively, and it is reset
3201 // whenever PRVM_ExecuteProgram returns to the engine
3202 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3203 //  restores it on return, so multiple recursive calls can share the same
3204 //  buffer)
3205 // the buffer size is automatically grown as needed
3206
3207 int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
3208 {
3209         int size;
3210         char *t;
3211         if (!s)
3212                 return 0;
3213         size = (int)strlen(s) + 1;
3214         if (developer_insane.integer)
3215                 Con_DPrintf("PRVM_SetTempString: cursize %i, size %i\n", prog->tempstringsbuf.cursize, size);
3216         if (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3217         {
3218                 sizebuf_t old = prog->tempstringsbuf;
3219                 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3220                         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);
3221                 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3222                 while (prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3223                         prog->tempstringsbuf.maxsize *= 2;
3224                 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3225                 {
3226                         Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3227                         prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3228                         if (old.data)
3229                         {
3230                                 if (old.cursize)
3231                                         memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3232                                 Mem_Free(old.data);
3233                         }
3234                 }
3235         }
3236         t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3237         memcpy(t, s, size);
3238         prog->tempstringsbuf.cursize += size;
3239         return PRVM_SetEngineString(prog, t);
3240 }
3241
3242 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3243 {
3244         int i;
3245         char *s;
3246         if (!bufferlength)
3247         {
3248                 if (pointer)
3249                         *pointer = NULL;
3250                 return 0;
3251         }
3252         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3253                 if (!prog->knownstrings[i])
3254                         break;
3255         s = (char *)PRVM_Alloc(bufferlength);
3256         PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3257         if(prog->leaktest_active)
3258                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3259         if (pointer)
3260                 *pointer = (char *)(prog->knownstrings[i]);
3261         return PRVM_KNOWNSTRINGBASE + i;
3262 }
3263
3264 void PRVM_FreeString(prvm_prog_t *prog, int num)
3265 {
3266         if (num == 0)
3267                 prog->error_cmd("PRVM_FreeString: attempt to free a NULL string");
3268         else if (num >= 0 && num < prog->stringssize)
3269                 prog->error_cmd("PRVM_FreeString: attempt to free a constant string");
3270         else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3271         {
3272                 num = num - PRVM_KNOWNSTRINGBASE;
3273                 if (!prog->knownstrings[num])
3274                         prog->error_cmd("PRVM_FreeString: attempt to free a non-existent or already freed string");
3275                 if (!prog->knownstrings_flags[num])
3276                         prog->error_cmd("PRVM_FreeString: attempt to free a string owned by the engine");
3277                 PRVM_Free((char *)prog->knownstrings[num]);
3278                 if(prog->leaktest_active)
3279                         if(prog->knownstrings_origin[num])
3280                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
3281                 prog->knownstrings[num] = NULL;
3282                 prog->knownstrings_flags[num] = 0;
3283                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3284         }
3285         else
3286                 prog->error_cmd("PRVM_FreeString: invalid string offset %i", num);
3287 }
3288
3289 static qboolean PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3290 {
3291         int i, j;
3292
3293         for (i = 0;i < prog->numglobaldefs;i++)
3294         {
3295                 ddef_t *d = &prog->globaldefs[i];
3296                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3297                         continue;
3298                 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3299                         return true;
3300         }
3301
3302         for(j = 0; j < prog->num_edicts; ++j)
3303         {
3304                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3305                 if (ed->priv.required->free)
3306                         continue;
3307                 for (i=0; i<prog->numfielddefs; ++i)
3308                 {
3309                         ddef_t *d = &prog->fielddefs[i];
3310                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3311                                 continue;
3312                         if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3313                                 return true;
3314                 }
3315         }
3316
3317         return false;
3318 }
3319
3320 static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3321 {
3322         char vabuf[1024];
3323         char vabuf2[1024];
3324         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3325                 return true; // world or clients
3326         if (edict->priv.required->freetime <= prog->inittime)
3327                 return true; // created during startup
3328         if (prog == SVVM_prog)
3329         {
3330                 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3331                         return true;
3332                 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3333                         return true;
3334                 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3335                         return true;
3336                 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3337                         if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3338                                 return true;
3339                 if(PRVM_serveredictfloat(edict, takedamage))
3340                         return true;
3341                 if(*prvm_leaktest_ignore_classnames.string)
3342                 {
3343                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3344                                 return true;
3345                 }
3346         }
3347         else if (prog == CLVM_prog)
3348         {
3349                 // TODO someone add more stuff here
3350                 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3351                         return true;
3352                 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3353                         return true;
3354                 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3355                         return true;
3356                 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3357                         if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3358                                 return true;
3359                 if(*prvm_leaktest_ignore_classnames.string)
3360                 {
3361                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3362                                 return true;
3363                 }
3364         }
3365         else
3366         {
3367                 // menu prog does not have classnames
3368         }
3369         return false;
3370 }
3371
3372 static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3373 {
3374         int i, j;
3375         int edictnum = PRVM_NUM_FOR_EDICT(edict);
3376         const char *targetname = NULL;
3377
3378         if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3379                 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3380
3381         if(targetname)
3382                 if(!*targetname) // ""
3383                         targetname = NULL;
3384
3385         for(j = 0; j < prog->num_edicts; ++j)
3386         {
3387                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3388                 if (ed->priv.required->mark < mark)
3389                         continue;
3390                 if(ed == edict)
3391                         continue;
3392                 if(targetname)
3393                 {
3394                         const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3395                         if(target)
3396                                 if(!strcmp(target, targetname))
3397                                         return true;
3398                 }
3399                 for (i=0; i<prog->numfielddefs; ++i)
3400                 {
3401                         ddef_t *d = &prog->fielddefs[i];
3402                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3403                                 continue;
3404                         if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3405                                 return true;
3406                 }
3407         }
3408
3409         return false;
3410 }
3411
3412 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3413 {
3414         int i, j;
3415         qboolean found_new;
3416         int stage;
3417
3418         // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3419         stage = 1;
3420         for(j = 0; j < prog->num_edicts; ++j)
3421         {
3422                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3423                 if(ed->priv.required->free)
3424                         continue;
3425                 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3426         }
3427         for (i = 0;i < prog->numglobaldefs;i++)
3428         {
3429                 ddef_t *d = &prog->globaldefs[i];
3430                 prvm_edict_t *ed;
3431                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3432                         continue;
3433                 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3434                 if (i < 0 || j >= prog->max_edicts) {
3435                         Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3436                         continue;
3437                 }
3438                 ed = PRVM_EDICT_NUM(j);;
3439                 ed->priv.required->mark = stage;
3440         }
3441
3442         // Future stages: all entities that are referenced by an entity of the previous stage.
3443         do
3444         {
3445                 found_new = false;
3446                 for(j = 0; j < prog->num_edicts; ++j)
3447                 {
3448                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3449                         if(ed->priv.required->free)
3450                                 continue;
3451                         if(ed->priv.required->mark)
3452                                 continue;
3453                         if(PRVM_IsEdictReferenced(prog, ed, stage))
3454                         {
3455                                 ed->priv.required->mark = stage + 1;
3456                                 found_new = true;
3457                         }
3458                 }
3459                 ++stage;
3460         }
3461         while(found_new);
3462         Con_DPrintf("leak check used %d stages to find all references\n", stage);
3463 }
3464
3465 void PRVM_LeakTest(prvm_prog_t *prog)
3466 {
3467         int i, j;
3468         qboolean leaked = false;
3469
3470         if(!prog->leaktest_active)
3471                 return;
3472
3473         // 1. Strings
3474         for (i = 0; i < prog->numknownstrings; ++i)
3475         {
3476                 if(prog->knownstrings[i])
3477                 if(prog->knownstrings_flags[i])
3478                 if(prog->knownstrings_origin[i])
3479                 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3480                 {
3481                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3482                         leaked = true;
3483                 }
3484         }
3485
3486         // 2. Edicts
3487         PRVM_MarkReferencedEdicts(prog);
3488         for(j = 0; j < prog->num_edicts; ++j)
3489         {
3490                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3491                 if(ed->priv.required->free)
3492                         continue;
3493                 if(!ed->priv.required->mark)
3494                 if(ed->priv.required->allocation_origin)
3495                 {
3496                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3497                         PRVM_ED_Print(prog, ed, NULL);
3498                         Con_Print("\n");
3499                         leaked = true;
3500                 }
3501
3502                 ed->priv.required->mark = 0; // clear marks again when done
3503         }
3504
3505         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3506         {
3507                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3508                 if(stringbuffer)
3509                 if(stringbuffer->origin)
3510                 {
3511                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3512                         leaked = true;
3513                 }
3514         }
3515
3516         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3517         {
3518                 if(prog->openfiles[i])
3519                 if(prog->openfiles_origin[i])
3520                 {
3521                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3522                         leaked = true;
3523                 }
3524         }
3525
3526         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3527         {
3528                 if(prog->opensearches[i])
3529                 if(prog->opensearches_origin[i])
3530                 {
3531                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3532                         leaked = true;
3533                 }
3534         }
3535
3536         if(!leaked)
3537                 Con_Printf("Congratulations. No leaks found.\n");
3538 }
3539
3540 void PRVM_GarbageCollection(prvm_prog_t *prog)
3541 {
3542         int limit = prvm_garbagecollection_scan_limit.integer;
3543         prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3544         if (!prvm_garbagecollection_enable.integer)
3545                 return;
3546         // philosophy:
3547         // we like to limit how much scanning we do so it doesn't put a significant
3548         // burden on the cpu, so each of these are not complete scans, we also like
3549         // to have consistent cpu usage so we do a bit of work on each category of
3550         // leaked object every frame
3551         switch (gc->stage)
3552         {
3553         case PRVM_GC_START:
3554                 gc->stage++;
3555                 break;
3556         case PRVM_GC_GLOBALS_MARK:
3557                 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3558                 {
3559                         ddef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3560                         switch (d->type)
3561                         {
3562                         case ev_string:
3563                                 {
3564                                         prvm_int_t s = prog->globals.ip[d->ofs];
3565                                         if (s & PRVM_KNOWNSTRINGBASE)
3566                                         {
3567                                                 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3568                                                 if (!prog->knownstrings[num])
3569                                                 {
3570                                                         // invalid
3571                                                         Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3572                                                         prog->globals.ip[d->ofs] = 0;
3573                                                         continue;
3574                                                 }
3575                                                 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3576                                         }
3577                                 }
3578                                 break;
3579                         default:
3580                                 break;
3581                         }
3582                 }
3583                 if (gc->globals_mark_progress >= prog->numglobaldefs)
3584                         gc->stage++;
3585                 break;
3586         case PRVM_GC_FIELDS_MARK:
3587                 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3588                 {
3589                         ddef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3590                         switch (d->type)
3591                         {
3592                         case ev_string:
3593                                 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3594                                 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3595                                 {
3596                                         int entityindex = gc->fields_mark_progress_entity;
3597                                         prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3598                                         if (s & PRVM_KNOWNSTRINGBASE)
3599                                         {
3600                                                 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3601                                                 if (!prog->knownstrings[num])
3602                                                 {
3603                                                         // invalid
3604                                                         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));
3605                                                         prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3606                                                         continue;
3607                                                 }
3608                                                 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3609                                         }
3610                                 }
3611                                 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3612                                 {
3613                                         gc->fields_mark_progress_entity = 0;
3614                                         gc->fields_mark_progress++;
3615                                 }
3616                                 break;
3617                         default:
3618                                 gc->fields_mark_progress_entity = 0;
3619                                 gc->fields_mark_progress++;
3620                                 break;
3621                         }
3622                 }
3623                 if (gc->fields_mark_progress >= prog->numfielddefs)
3624                         gc->stage++;
3625                 break;
3626         case PRVM_GC_KNOWNSTRINGS_SWEEP:
3627                 // free any strzone'd strings that are not marked
3628                 if (!prvm_garbagecollection_strings.integer)
3629                 {
3630                         gc->stage++;
3631                         break;
3632                 }
3633                 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3634                 {
3635                         int num = gc->knownstrings_sweep_progress;
3636                         if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3637                         {
3638                                 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3639                                 {
3640                                         // string has been marked for pruning two passes in a row
3641                                         if (prvm_garbagecollection_notify.integer)
3642                                                 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3643                                         Mem_Free((char *)prog->knownstrings[num]);
3644                                         prog->knownstrings[num] = NULL;
3645                                         prog->knownstrings_flags[num] = 0;
3646                                         prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3647                                 }
3648                                 else
3649                                 {
3650                                         // mark it for pruning next pass
3651                                         prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3652                                 }
3653                         }
3654                 }
3655                 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3656                         gc->stage++;
3657                 break;
3658         case PRVM_GC_RESET:
3659         default:
3660                 memset(gc, 0, sizeof(*gc));
3661         }
3662 }