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