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