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