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