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