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