]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
PRVM: Fix a hack in LNO file loading
[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_LoadProgs
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, int numrequiredfunc, const char **required_func, 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("PRVM_LoadProgs: there is already a %s program loaded!", 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("PRVM_LoadProgs: couldn't load %s for %s", 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("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", 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("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", 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("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", 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("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", 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("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", 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("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (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("PRVM_LoadProgs: out of bounds global index (statement %d)", 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("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", 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("PRVM_LoadProgs: storep-with-offset is not permitted in %s\n", 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("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", 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("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, prog->name);
2469                         if (b || c)     //Spike -- added this check just as a diagnostic...
2470                                 Con_DPrintf("PRVM_LoadProgs: unexpected offset on call opcode in %s. Hexen2 format is not supported\n", 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("PRVM_LoadProgs: empty program in %s", 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("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", 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         // check required functions
2500         for(i=0 ; i < numrequiredfunc ; i++)
2501                 if(PRVM_ED_FindFunction(prog, required_func[i]) == 0)
2502                         prog->error_cmd("%s: %s not found in %s",prog->name, required_func[i], filename);
2503
2504         PRVM_LoadLNO(prog, filename);
2505
2506         PRVM_Init_Exec(prog);
2507
2508         if(*prvm_language.string)
2509         // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2510         // later idea: include a list of authorized .po file checksums with the csprogs
2511         {
2512                 qbool deftrans = prog == CLVM_prog;
2513                 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2514                 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2515                 {
2516                         for (i=0 ; i<prog->numglobaldefs ; i++)
2517                         {
2518                                 const char *name;
2519                                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2520                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2521                                 if(name && !strncmp(name, "dotranslate_", 12))
2522                                 {
2523                                         deftrans = false;
2524                                         break;
2525                                 }
2526                         }
2527                 }
2528                 if(!strcmp(prvm_language.string, "dump"))
2529                 {
2530                         qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2531                         Con_Printf("Dumping to %s.pot\n", realfilename);
2532                         if(f)
2533                         {
2534                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2535                                 {
2536                                         const char *name;
2537                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2538                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2539                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2540                                         {
2541                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2542                                                 const char *value = PRVM_GetString(prog, val->string);
2543                                                 if(*value)
2544                                                 {
2545                                                         char buf[MAX_INPUTLINE];
2546                                                         PRVM_PO_UnparseString(buf, value, sizeof(buf));
2547                                                         FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2548                                                 }
2549                                         }
2550                                 }
2551                                 FS_Close(f);
2552                         }
2553                 }
2554                 else
2555                 {
2556                         po_t *po = PRVM_PO_Load(
2557                                         va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2558                                         va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2559                                         prog->progs_mempool);
2560                         if(po)
2561                         {
2562                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2563                                 {
2564                                         const char *name;
2565                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2566                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2567                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2568                                         {
2569                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2570                                                 const char *value = PRVM_GetString(prog, val->string);
2571                                                 if(*value)
2572                                                 {
2573                                                         value = PRVM_PO_Lookup(po, value);
2574                                                         if(value)
2575                                                                 val->string = PRVM_SetEngineString(prog, value);
2576                                                 }
2577                                         }
2578                                 }
2579                         }
2580                 }
2581         }
2582
2583         for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2584                 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2585
2586         for (i=0 ; i<prog->numglobaldefs ; i++)
2587         {
2588                 const char *name;
2589                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2590                 //Con_Printf("found var %s\n", name);
2591                 if(name
2592                         && !strncmp(name, "autocvar_", 9)
2593                         && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2594                 )
2595                 {
2596                         prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2597                         cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2598                         //Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
2599                         if(!cvar)
2600                         {
2601                                 const char *value;
2602                                 char buf[128];
2603                                 int prec[3];
2604                                 float f;
2605                                 Con_DPrintf("PRVM_LoadProgs: no cvar for autocvar global %s in %s, creating...\n", name, prog->name);
2606                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2607                                 {
2608                                         case ev_float:
2609                                                 if((float)((int)(val->_float)) == val->_float)
2610                                                         dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2611                                                 else
2612                                                 {
2613                                                         // ftos_slow
2614                                                         f = val->_float;
2615                                                         for (int precision = 7; precision <= 9; ++precision) {
2616                                                                 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2617                                                                 if ((float)atof(buf) == f) {
2618                                                                         break;
2619                                                                 }
2620                                                         }
2621                                                 }
2622                                                 value = buf;
2623                                                 break;
2624                                         case ev_vector:
2625                                                 for (i = 0; i < 3; ++i)
2626                                                 {
2627                                                         prec[i] = 9;
2628                                                         f = val->vector[i];
2629                                                         for (int precision = 7; precision <= 9; ++precision) {
2630                                                                 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2631                                                                 if ((float)atof(buf) == f) {
2632                                                                         prec[i] = precision;
2633                                                                         break;
2634                                                                 }
2635                                                         }
2636                                                 }
2637                                                 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2638                                                 value = buf;
2639                                                 break;
2640                                         case ev_string:
2641                                                 value = PRVM_GetString(prog, val->string);
2642                                                 break;
2643                                         default:
2644                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2645                                                 goto fail;
2646                                 }
2647                                 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2648                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2649                                 {
2650                                         val->string = PRVM_SetEngineString(prog, cvar->string);
2651                                         cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2652                                 }
2653                                 if(!cvar)
2654                                         prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
2655                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2656                         }
2657                         else if((cvar->flags & CF_PRIVATE) == 0)
2658                         {
2659                                 // MUST BE SYNCED WITH cvar.c Cvar_Set
2660                                 int j;
2661                                 const char *s;
2662                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2663                                 {
2664                                         case ev_float:
2665                                                 val->_float = cvar->value;
2666                                                 break;
2667                                         case ev_vector:
2668                                                 s = cvar->string;
2669                                                 VectorClear(val->vector);
2670                                                 for (j = 0;j < 3;j++)
2671                                                 {
2672                                                         while (*s && ISWHITESPACE(*s))
2673                                                                 s++;
2674                                                         if (!*s)
2675                                                                 break;
2676                                                         val->vector[j] = atof(s);
2677                                                         while (!ISWHITESPACE(*s))
2678                                                                 s++;
2679                                                         if (!*s)
2680                                                                 break;
2681                                                 }
2682                                                 break;
2683                                         case ev_string:
2684                                                 val->string = PRVM_SetEngineString(prog, cvar->string);
2685                                                 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2686                                                 break;
2687                                         default:
2688                                                 Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
2689                                                 goto fail;
2690                                 }
2691                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2692                         }
2693                         else
2694                                 Con_Printf("PRVM_LoadProgs: private cvar for autocvar global %s in %s\n", name, prog->name);
2695                 }
2696 fail:
2697                 ;
2698         }
2699
2700         prog->loaded = true;
2701
2702         PRVM_UpdateBreakpoints(prog);
2703
2704         // set flags & mdef_ts in prog
2705
2706         prog->flag = 0;
2707
2708         PRVM_FindOffsets(prog);
2709
2710         prog->init_cmd(prog);
2711
2712         // init mempools
2713         PRVM_MEM_Alloc(prog);
2714
2715         Con_Printf("%s: program loaded (crc %i, size %iK)\n", prog->name, prog->filecrc, (int)(filesize/1024));
2716
2717         // Inittime is at least the time when this function finished. However,
2718         // later events may bump it.
2719         prog->inittime = host.realtime;
2720 }
2721
2722
2723 static void PRVM_Fields_f(cmd_state_t *cmd)
2724 {
2725         prvm_prog_t *prog;
2726         int i, j, ednum, used, usedamount;
2727         int *counts;
2728         char tempstring[MAX_INPUTLINE], tempstring2[260];
2729         const char *name;
2730         prvm_edict_t *ed;
2731         mdef_t *d;
2732         prvm_eval_t *val;
2733
2734         // TODO
2735         /*
2736         if (!sv.active)
2737         {
2738                 Con_Print("no progs loaded\n");
2739                 return;
2740         }
2741         */
2742
2743         if(Cmd_Argc(cmd) != 2)
2744         {
2745                 Con_Print("prvm_fields <program name>\n");
2746                 return;
2747         }
2748
2749         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2750                 return;
2751
2752         counts = (int *)Mem_Alloc(tempmempool, prog->numfielddefs * sizeof(int));
2753         for (ednum = 0;ednum < prog->max_edicts;ednum++)
2754         {
2755                 ed = PRVM_EDICT_NUM(ednum);
2756                 if (ed->free)
2757                         continue;
2758                 for (i = 1;i < prog->numfielddefs;i++)
2759                 {
2760                         d = &prog->fielddefs[i];
2761                         name = PRVM_GetString(prog, d->s_name);
2762                         if (name[strlen(name)-2] == '_')
2763                                 continue;       // skip _x, _y, _z vars
2764                         val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
2765                         // if the value is still all 0, skip the field
2766                         for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2767                         {
2768                                 if (val->ivector[j])
2769                                 {
2770                                         counts[i]++;
2771                                         break;
2772                                 }
2773                         }
2774                 }
2775         }
2776         used = 0;
2777         usedamount = 0;
2778         tempstring[0] = 0;
2779         for (i = 0;i < prog->numfielddefs;i++)
2780         {
2781                 d = &prog->fielddefs[i];
2782                 name = PRVM_GetString(prog, d->s_name);
2783                 if (name[strlen(name)-2] == '_')
2784                         continue;       // skip _x, _y, _z vars
2785                 switch(d->type & ~DEF_SAVEGLOBAL)
2786                 {
2787                 case ev_string:
2788                         dp_strlcat(tempstring, "string   ", sizeof(tempstring));
2789                         break;
2790                 case ev_entity:
2791                         dp_strlcat(tempstring, "entity   ", sizeof(tempstring));
2792                         break;
2793                 case ev_function:
2794                         dp_strlcat(tempstring, "function ", sizeof(tempstring));
2795                         break;
2796                 case ev_field:
2797                         dp_strlcat(tempstring, "field    ", sizeof(tempstring));
2798                         break;
2799                 case ev_void:
2800                         dp_strlcat(tempstring, "void     ", sizeof(tempstring));
2801                         break;
2802                 case ev_float:
2803                         dp_strlcat(tempstring, "float    ", sizeof(tempstring));
2804                         break;
2805                 case ev_vector:
2806                         dp_strlcat(tempstring, "vector   ", sizeof(tempstring));
2807                         break;
2808                 case ev_pointer:
2809                         dp_strlcat(tempstring, "pointer  ", sizeof(tempstring));
2810                         break;
2811                 default:
2812                         dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2813                         dp_strlcat(tempstring, tempstring2, sizeof(tempstring));
2814                         break;
2815                 }
2816                 if (strlen(name) > sizeof(tempstring2)-4)
2817                 {
2818                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
2819                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2820                         tempstring2[sizeof(tempstring2)-1] = 0;
2821                         name = tempstring2;
2822                 }
2823                 dp_strlcat(tempstring, name, sizeof(tempstring));
2824                 for (j = (int)strlen(name);j < 25;j++)
2825                         dp_strlcat(tempstring, " ", sizeof(tempstring));
2826                 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2827                 dp_strlcat(tempstring, tempstring2, sizeof(tempstring));
2828                 dp_strlcat(tempstring, "\n", sizeof(tempstring));
2829                 if (strlen(tempstring) >= sizeof(tempstring)/2)
2830                 {
2831                         Con_Print(tempstring);
2832                         tempstring[0] = 0;
2833                 }
2834                 if (counts[i])
2835                 {
2836                         used++;
2837                         usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2838                 }
2839         }
2840         Mem_Free(counts);
2841         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);
2842 }
2843
2844 static void PRVM_Globals_f(cmd_state_t *cmd)
2845 {
2846         prvm_prog_t *prog;
2847         int i;
2848         const char *wildcard;
2849         int numculled;
2850                 numculled = 0;
2851         // TODO
2852         /*if (!sv.active)
2853         {
2854                 Con_Print("no progs loaded\n");
2855                 return;
2856         }*/
2857         if(Cmd_Argc (cmd) < 2 || Cmd_Argc(cmd) > 3)
2858         {
2859                 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2860                 return;
2861         }
2862
2863         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2864                 return;
2865
2866         if( Cmd_Argc(cmd) == 3)
2867                 wildcard = Cmd_Argv(cmd, 2);
2868         else
2869                 wildcard = NULL;
2870
2871         Con_Printf("%s :", prog->name);
2872
2873         for (i = 0;i < prog->numglobaldefs;i++)
2874         {
2875                 if(wildcard)
2876                         if( !matchpattern( PRVM_GetString(prog, prog->globaldefs[i].s_name), wildcard, 1) )
2877                         {
2878                                 numculled++;
2879                                 continue;
2880                         }
2881                 Con_Printf("%s\n", PRVM_GetString(prog, prog->globaldefs[i].s_name));
2882         }
2883         Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->numglobals, numculled, prog->numglobals * 4);
2884 }
2885
2886 /*
2887 ===============
2888 PRVM_Global
2889 ===============
2890 */
2891 static void PRVM_Global_f(cmd_state_t *cmd)
2892 {
2893         prvm_prog_t *prog;
2894         mdef_t *global;
2895         char valuebuf[MAX_INPUTLINE];
2896         if( Cmd_Argc(cmd) != 3 ) {
2897                 Con_Printf( "prvm_global <program name> <global name>\n" );
2898                 return;
2899         }
2900
2901         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2902                 return;
2903
2904         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2905         if( !global )
2906                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2907         else
2908                 Con_Printf( "%s: %s\n", Cmd_Argv(cmd, 2), PRVM_ValueString( prog, (etype_t)global->type, PRVM_GLOBALFIELDVALUE(global->ofs), valuebuf, sizeof(valuebuf) ) );
2909 }
2910
2911 /*
2912 ===============
2913 PRVM_GlobalSet
2914 ===============
2915 */
2916 static void PRVM_GlobalSet_f(cmd_state_t *cmd)
2917 {
2918         prvm_prog_t *prog;
2919         mdef_t *global;
2920         if( Cmd_Argc(cmd) != 4 ) {
2921                 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2922                 return;
2923         }
2924
2925         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
2926                 return;
2927
2928         global = PRVM_ED_FindGlobal( prog, Cmd_Argv(cmd, 2) );
2929         if( !global )
2930                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
2931         else
2932                 PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(cmd, 3), true );
2933 }
2934
2935 /*
2936 ======================
2937 Break- and Watchpoints
2938 ======================
2939 */
2940 typedef struct
2941 {
2942         char break_statement[256];
2943         char watch_global[256];
2944         int watch_edict;
2945         char watch_field[256];
2946 }
2947 debug_data_t;
2948 static debug_data_t debug_data[PRVM_PROG_MAX];
2949
2950 void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text)
2951 {
2952         char vabuf[1024];
2953         Con_Printf("PRVM_Breakpoint: %s\n", text);
2954         PRVM_PrintState(prog, stack_index);
2955         if (prvm_breakpointdump.integer)
2956                 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
2957 }
2958
2959 void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
2960 {
2961         size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
2962         if (memcmp(o, n, sz))
2963         {
2964                 char buf[1024];
2965                 char valuebuf_o[128];
2966                 char valuebuf_n[128];
2967                 PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
2968                 PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
2969                 dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
2970                 PRVM_Breakpoint(prog, stack_index, buf);
2971                 memcpy(o, n, sz);
2972         }
2973 }
2974
2975 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
2976 {
2977         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
2978         if (!prog->loaded)
2979                 return;
2980         if (debug->break_statement[0])
2981         {
2982                 if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9')
2983                 {
2984                         prog->break_statement = atoi(debug->break_statement);
2985                         prog->break_stack_index = 0;
2986                 }
2987                 else
2988                 {
2989                         mfunction_t *func;
2990                         func = PRVM_ED_FindFunction (prog, debug->break_statement);
2991                         if (!func)
2992                         {
2993                                 Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement);
2994                                 prog->break_statement = -1;
2995                         }
2996                         else
2997                         {
2998                                 prog->break_statement = func->first_statement;
2999                                 prog->break_stack_index = 1;
3000                         }
3001                 }
3002                 if (prog->break_statement >= -1)
3003                         Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement);
3004         }
3005         else
3006                 prog->break_statement = -1;
3007
3008         if (debug->watch_global[0])
3009         {
3010                 mdef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global );
3011                 if( !global )
3012                 {
3013                         Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
3014                         prog->watch_global_type = ev_void;
3015                 }
3016                 else
3017                 {
3018                         size_t sz = sizeof(prvm_vec_t) * ((global->type  & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
3019                         prog->watch_global = global->ofs;
3020                         prog->watch_global_type = (etype_t)global->type;
3021                         memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
3022                 }
3023                 if (prog->watch_global_type != ev_void)
3024                         Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
3025         }
3026         else
3027                 prog->watch_global_type = ev_void;
3028
3029         if (debug->watch_field[0])
3030         {
3031                 mdef_t *field = PRVM_ED_FindField( prog, debug->watch_field );
3032                 if( !field )
3033                 {
3034                         Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
3035                         prog->watch_field_type = ev_void;
3036                 }
3037                 else
3038                 {
3039                         size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
3040                         prog->watch_edict = debug->watch_edict;
3041                         prog->watch_field = field->ofs;
3042                         prog->watch_field_type = (etype_t)field->type;
3043                         if (prog->watch_edict < prog->num_edicts)
3044                                 memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
3045                         else
3046                                 memset(&prog->watch_edictfield_value, 0, sz);
3047                 }
3048                 if (prog->watch_edict != ev_void)
3049                         Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
3050         }
3051         else
3052                 prog->watch_field_type = ev_void;
3053 }
3054
3055 static void PRVM_Breakpoint_f(cmd_state_t *cmd)
3056 {
3057         prvm_prog_t *prog;
3058
3059         if( Cmd_Argc(cmd) == 2 ) {
3060                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
3061                         return;
3062                 {
3063                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3064                         debug->break_statement[0] = 0;
3065                 }
3066                 PRVM_UpdateBreakpoints(prog);
3067                 return;
3068         }
3069         if( Cmd_Argc(cmd) != 3 ) {
3070                 Con_Printf( "prvm_breakpoint <program name> <function name | statement>\n" );
3071                 return;
3072         }
3073
3074         if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
3075                 return;
3076
3077         {
3078                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3079                 dp_strlcpy(debug->break_statement, Cmd_Argv(cmd, 2), sizeof(debug->break_statement));
3080         }
3081         PRVM_UpdateBreakpoints(prog);
3082 }
3083
3084 static void PRVM_GlobalWatchpoint_f(cmd_state_t *cmd)
3085 {
3086         prvm_prog_t *prog;
3087
3088         if( Cmd_Argc(cmd) == 2 ) {
3089                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
3090                         return;
3091                 {
3092                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3093                         debug->watch_global[0] = 0;
3094                 }
3095                 PRVM_UpdateBreakpoints(prog);
3096                 return;
3097         }
3098         if( Cmd_Argc(cmd) != 3 ) {
3099                 Con_Printf( "prvm_globalwatchpoint <program name> <global name>\n" );
3100                 return;
3101         }
3102
3103         if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
3104                 return;
3105
3106         {
3107                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3108                 dp_strlcpy(debug->watch_global, Cmd_Argv(cmd, 2), sizeof(debug->watch_global));
3109         }
3110         PRVM_UpdateBreakpoints(prog);
3111 }
3112
3113 static void PRVM_EdictWatchpoint_f(cmd_state_t *cmd)
3114 {
3115         prvm_prog_t *prog;
3116
3117         if( Cmd_Argc(cmd) == 2 ) {
3118                 if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
3119                         return;
3120                 {
3121                         debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3122                         debug->watch_field[0] = 0;
3123                 }
3124                 PRVM_UpdateBreakpoints(prog);
3125                 return;
3126         }
3127         if( Cmd_Argc(cmd) != 4 ) {
3128                 Con_Printf( "prvm_edictwatchpoint <program name> <edict number> <field name>\n" );
3129                 return;
3130         }
3131
3132         if (!(prog = PRVM_ProgFromString(Cmd_Argv(cmd, 1))))
3133                 return;
3134
3135         {
3136                 debug_data_t *debug = &debug_data[prog - prvm_prog_list];
3137                 debug->watch_edict = atoi(Cmd_Argv(cmd, 2));
3138                 dp_strlcpy(debug->watch_field, Cmd_Argv(cmd, 3), sizeof(debug->watch_field));
3139         }
3140         PRVM_UpdateBreakpoints(prog);
3141 }
3142
3143 /*
3144 ===============
3145 PRVM_Init
3146 ===============
3147 */
3148 void PRVM_Init (void)
3149 {
3150         unsigned int i;
3151
3152         Cmd_AddCommand(CF_SHARED, "prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
3153         Cmd_AddCommand(CF_SHARED, "prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
3154         Cmd_AddCommand(CF_SHARED, "prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
3155         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)");
3156         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");
3157         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)");
3158         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)");
3159         Cmd_AddCommand(CF_SHARED, "prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
3160         Cmd_AddCommand(CF_SHARED, "prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
3161         Cmd_AddCommand(CF_SHARED, "prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
3162         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)");
3163         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");
3164         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");
3165         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)");
3166         Cmd_AddCommand(CF_SHARED, "cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
3167         Cmd_AddCommand(CF_SHARED, "menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
3168         Cmd_AddCommand(CF_SHARED, "sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
3169         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");
3170         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");
3171         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");
3172
3173         Cvar_RegisterVariable (&prvm_language);
3174         Cvar_RegisterVariable (&prvm_traceqc);
3175         Cvar_RegisterVariable (&prvm_statementprofiling);
3176         Cvar_RegisterVariable (&prvm_timeprofiling);
3177         Cvar_RegisterVariable (&prvm_coverage);
3178         Cvar_RegisterVariable (&prvm_backtraceforwarnings);
3179         Cvar_RegisterVariable (&prvm_leaktest);
3180         Cvar_RegisterVariable (&prvm_leaktest_follow_targetname);
3181         Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
3182         Cvar_RegisterVariable (&prvm_errordump);
3183         Cvar_RegisterVariable (&prvm_breakpointdump);
3184         Cvar_RegisterVariable (&prvm_reuseedicts_startuptime);
3185         Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe);
3186         Cvar_RegisterVariable (&prvm_garbagecollection_enable);
3187         Cvar_RegisterVariable (&prvm_garbagecollection_notify);
3188         Cvar_RegisterVariable (&prvm_garbagecollection_scan_limit);
3189         Cvar_RegisterVariable (&prvm_garbagecollection_strings);
3190         Cvar_RegisterVariable (&prvm_stringdebug);
3191
3192         // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
3193         prvm_runawaycheck = !Sys_CheckParm("-norunaway");
3194
3195         //VM_Cmd_Init();
3196
3197         // LadyHavoc: report supported extensions
3198         Con_DPrintf("\nQuakeC extensions for server and client:");
3199         for (i = 0; vm_sv_extensions[i]; i++)
3200                 Con_DPrintf(" %s", vm_sv_extensions[i]);
3201         Con_DPrintf("\n");
3202 #ifdef CONFIG_MENU
3203         Con_DPrintf("\nQuakeC extensions for menu:");
3204         for (i = 0; vm_m_extensions[i]; i++)
3205                 Con_DPrintf(" %s", vm_m_extensions[i]);
3206         Con_DPrintf("\n");
3207 #endif
3208 }
3209
3210 /*
3211 ===============
3212 PRVM_InitProg
3213 ===============
3214 */
3215 void PRVM_Prog_Init(prvm_prog_t *prog, cmd_state_t *cmd)
3216 {
3217         PRVM_Prog_Reset(prog);
3218         prog->leaktest_active = prvm_leaktest.integer != 0;
3219         prog->console_cmd = cmd;
3220 }
3221
3222 // LadyHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
3223 unsigned int PRVM_EDICT_NUM_ERROR(prvm_prog_t *prog, unsigned int n, const char *filename, int fileline)
3224 {
3225         prog->error_cmd("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", prog->name, n, filename, fileline);
3226         return 0;
3227 }
3228
3229 #define PRVM_KNOWNSTRINGBASE 0x40000000
3230
3231 const char *PRVM_GetString(prvm_prog_t *prog, int num)
3232 {
3233         if (num < 0)
3234         {
3235                 // invalid
3236                 if (prvm_stringdebug.integer)
3237                         VM_Warning(prog, "PRVM_GetString: Invalid string offset (%i < 0)\n", num);
3238                 return "";
3239         }
3240         else if (num < prog->stringssize)
3241         {
3242                 // constant string from progs.dat
3243                 return prog->strings + num;
3244         }
3245         else if (num <= prog->stringssize + prog->tempstringsbuf.maxsize)
3246         {
3247                 // tempstring returned by engine to QC (becomes invalid after returning to engine)
3248                 num -= prog->stringssize;
3249                 if (num < prog->tempstringsbuf.cursize)
3250                         return (char *)prog->tempstringsbuf.data + num;
3251                 else
3252                 {
3253                         if (prvm_stringdebug.integer)
3254                                 VM_Warning(prog, "PRVM_GetString: Invalid temp-string offset (%i >= %i prog->tempstringsbuf.cursize)\n", num, prog->tempstringsbuf.cursize);
3255                         return "";
3256                 }
3257         }
3258         else if (num & PRVM_KNOWNSTRINGBASE)
3259         {
3260                 // allocated string
3261                 num = num - PRVM_KNOWNSTRINGBASE;
3262                 if (num >= 0 && num < prog->numknownstrings)
3263                 {
3264                         if (!prog->knownstrings[num])
3265                         {
3266                                 if (prvm_stringdebug.integer)
3267                                         VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
3268                                 return "";
3269                         }
3270                         // refresh the garbage collection on the string - this guards
3271                         // against a certain sort of repeated migration to earlier
3272                         // points in the scan that could otherwise result in the string
3273                         // being freed for being unused
3274                         prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] & ~KNOWNSTRINGFLAG_GCPRUNE) | KNOWNSTRINGFLAG_GCMARK;
3275                         return prog->knownstrings[num];
3276                 }
3277                 else
3278                 {
3279                         if (prvm_stringdebug.integer)
3280                                 VM_Warning(prog, "PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
3281                         return "";
3282                 }
3283         }
3284         else
3285         {
3286                 // invalid string offset
3287                 if (prvm_stringdebug.integer)
3288                         VM_Warning(prog, "PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
3289                 return "";
3290         }
3291 }
3292
3293 const char *PRVM_ChangeEngineString(prvm_prog_t *prog, int i, const char *s)
3294 {
3295         const char *old;
3296         i = i - PRVM_KNOWNSTRINGBASE;
3297         if (i < 0 || i >= prog->numknownstrings)
3298                 prog->error_cmd("PRVM_ChangeEngineString: string index %i is out of bounds", i);
3299         else if ((prog->knownstrings_flags[i] & KNOWNSTRINGFLAG_ENGINE) == 0)
3300                 prog->error_cmd("PRVM_ChangeEngineString: string index %i is not an engine string", i);
3301         old = prog->knownstrings[i];
3302         prog->knownstrings[i] = s;
3303         return old;
3304 }
3305
3306 static void PRVM_NewKnownString(prvm_prog_t *prog, int i, int flags, const char *s)
3307 {
3308         if (i >= prog->numknownstrings)
3309         {
3310                 if (i >= prog->maxknownstrings)
3311                 {
3312                         const char **oldstrings = prog->knownstrings;
3313                         const unsigned char *oldstrings_flags = prog->knownstrings_flags;
3314                         const char **oldstrings_origin = prog->knownstrings_origin;
3315                         prog->maxknownstrings += 128;
3316                         prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3317                         prog->knownstrings_flags = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
3318                         if (prog->leaktest_active)
3319                                 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
3320                         if (prog->numknownstrings)
3321                         {
3322                                 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
3323                                 memcpy((char **)prog->knownstrings_flags, oldstrings_flags, prog->numknownstrings * sizeof(unsigned char));
3324                                 if (prog->leaktest_active)
3325                                         memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
3326                         }
3327                 }
3328                 prog->numknownstrings++;
3329         }
3330         prog->firstfreeknownstring = i + 1;
3331         prog->knownstrings[i] = s;
3332         // it's in use right now, spare it until the next gc pass - that said, it is not freeable so this is probably moot
3333         prog->knownstrings_flags[i] = flags;
3334         if (prog->leaktest_active)
3335                 prog->knownstrings_origin[i] = NULL;
3336 }
3337
3338 int PRVM_SetEngineString(prvm_prog_t *prog, const char *s)
3339 {
3340         int i;
3341         if (!s)
3342                 return 0;
3343         if (s >= prog->strings && s <= prog->strings + prog->stringssize)
3344                 prog->error_cmd("PRVM_SetEngineString: s in prog->strings area");
3345         // if it's in the tempstrings area, use a reserved range
3346         // (otherwise we'd get millions of useless string offsets cluttering the database)
3347         if (s >= (char *)prog->tempstringsbuf.data && s < (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.maxsize)
3348                 return prog->stringssize + (s - (char *)prog->tempstringsbuf.data);
3349         // see if it's a known string address
3350         for (i = 0;i < prog->numknownstrings;i++)
3351                 if (prog->knownstrings[i] == s)
3352                         return PRVM_KNOWNSTRINGBASE + i;
3353         // new unknown engine string
3354         if (developer_insane.integer)
3355                 Con_DPrintf("new engine string %p = \"%s\"\n", (void *)s, s);
3356         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3357                 if (!prog->knownstrings[i])
3358                         break;
3359         PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE, s);
3360         return PRVM_KNOWNSTRINGBASE + i;
3361 }
3362
3363 // temp string handling
3364
3365 // all tempstrings go into this buffer consecutively, and it is reset
3366 // whenever PRVM_ExecuteProgram returns to the engine
3367 // (technically each PRVM_ExecuteProgram call saves the cursize value and
3368 //  restores it on return, so multiple recursive calls can share the same
3369 //  buffer)
3370 // the buffer size is automatically grown as needed
3371 int PRVM_SetTempString(prvm_prog_t *prog, const char *s, size_t slen)
3372 {
3373         size_t size;
3374         char *t;
3375
3376         if (!s || slen >= VM_TEMPSTRING_MAXSIZE)
3377                 return 0;
3378         size = slen + 1;
3379         if (developer_insane.integer)
3380                 Con_DPrintf("PRVM_SetTempString %s: cursize %i, size %zu\n", prog->name, prog->tempstringsbuf.cursize, size);
3381         if ((size_t)prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3382         {
3383                 sizebuf_t old = prog->tempstringsbuf;
3384                 if (prog->tempstringsbuf.cursize + size >= 1<<28)
3385                         prog->error_cmd("PRVM_SetTempString %s: ran out of tempstring memory!  (refusing to grow tempstring buffer over 256MB, cursize %i, size %zu)\n", prog->name, prog->tempstringsbuf.cursize, size);
3386                 prog->tempstringsbuf.maxsize = max(prog->tempstringsbuf.maxsize, 65536);
3387                 while ((size_t)prog->tempstringsbuf.maxsize < prog->tempstringsbuf.cursize + size)
3388                         prog->tempstringsbuf.maxsize *= 2;
3389                 if (prog->tempstringsbuf.maxsize != old.maxsize || prog->tempstringsbuf.data == NULL)
3390                 {
3391                         Con_DPrintf("PRVM_SetTempString %s: enlarging tempstrings buffer (%iKB -> %iKB)\n", prog->name, old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
3392                         prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
3393                         if (old.data)
3394                         {
3395                                 if (old.cursize)
3396                                         memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
3397                                 Mem_Free(old.data);
3398                         }
3399                 }
3400         }
3401         t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
3402         memcpy(t, s, size);
3403         prog->tempstringsbuf.cursize += size;
3404         return PRVM_SetEngineString(prog, t);
3405 }
3406
3407 int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
3408 {
3409         int i;
3410         char *s;
3411         if (!bufferlength)
3412         {
3413                 if (pointer)
3414                         *pointer = NULL;
3415                 return 0;
3416         }
3417         for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
3418                 if (!prog->knownstrings[i])
3419                         break;
3420         s = (char *)PRVM_Alloc(bufferlength);
3421         PRVM_NewKnownString(prog, i, KNOWNSTRINGFLAG_GCMARK, s);
3422         if(prog->leaktest_active)
3423                 prog->knownstrings_origin[i] = PRVM_AllocationOrigin(prog);
3424         if (pointer)
3425                 *pointer = (char *)(prog->knownstrings[i]);
3426         return PRVM_KNOWNSTRINGBASE + i;
3427 }
3428
3429 void PRVM_FreeString(prvm_prog_t *prog, int num)
3430 {
3431         if (num == 0)
3432                 prog->error_cmd("PRVM_FreeString %s: attempt to free a NULL string", prog->name);
3433         else if (num >= 0 && num < prog->stringssize)
3434                 prog->error_cmd("PRVM_FreeString %s: attempt to free a constant string", prog->name);
3435         else if (num >= PRVM_KNOWNSTRINGBASE && num < PRVM_KNOWNSTRINGBASE + prog->numknownstrings)
3436         {
3437                 num = num - PRVM_KNOWNSTRINGBASE;
3438                 if (!prog->knownstrings[num])
3439                         prog->error_cmd("PRVM_FreeString %s: attempt to free a non-existent or already freed string", prog->name);
3440                 if (!prog->knownstrings_flags[num])
3441                         prog->error_cmd("PRVM_FreeString %s: attempt to free a string owned by the engine", prog->name);
3442                 PRVM_Free((char *)prog->knownstrings[num]);
3443                 if(prog->leaktest_active)
3444                         if(prog->knownstrings_origin[num])
3445                                 PRVM_Free((char *)prog->knownstrings_origin[num]);
3446                 prog->knownstrings[num] = NULL;
3447                 prog->knownstrings_flags[num] = 0;
3448                 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3449         }
3450         else
3451                 prog->error_cmd("PRVM_FreeString %s: invalid string offset %i", prog->name, num);
3452 }
3453
3454 static qbool PRVM_IsStringReferenced(prvm_prog_t *prog, string_t string)
3455 {
3456         int i, j;
3457
3458         for (i = 0;i < prog->numglobaldefs;i++)
3459         {
3460                 mdef_t *d = &prog->globaldefs[i];
3461                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3462                         continue;
3463                 if(string == PRVM_GLOBALFIELDSTRING(d->ofs))
3464                         return true;
3465         }
3466
3467         for(j = 0; j < prog->num_edicts; ++j)
3468         {
3469                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3470                 if (ed->free)
3471                         continue;
3472                 for (i=0; i<prog->numfielddefs; ++i)
3473                 {
3474                         mdef_t *d = &prog->fielddefs[i];
3475                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
3476                                 continue;
3477                         if(string == PRVM_EDICTFIELDSTRING(ed, d->ofs))
3478                                 return true;
3479                 }
3480         }
3481
3482         return false;
3483 }
3484
3485 static qbool PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict)
3486 {
3487         char vabuf[1024];
3488         char vabuf2[1024];
3489         if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
3490                 return true; // world or clients
3491         if (edict->freetime <= prog->inittime)
3492                 return true; // created during startup
3493         if (prog == SVVM_prog)
3494         {
3495                 if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger?
3496                         return true;
3497                 if(PRVM_serveredictfloat(edict, modelindex)) // visible ent?
3498                         return true;
3499                 if(PRVM_serveredictfloat(edict, effects)) // particle effect?
3500                         return true;
3501                 if(PRVM_serveredictfunction(edict, think)) // has a think function?
3502                         if(PRVM_serveredictfloat(edict, nextthink) > 0) // that actually will eventually run?
3503                                 return true;
3504                 if(PRVM_serveredictfloat(edict, takedamage))
3505                         return true;
3506                 if(*prvm_leaktest_ignore_classnames.string)
3507                 {
3508                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)))))
3509                                 return true;
3510                 }
3511         }
3512         else if (prog == CLVM_prog)
3513         {
3514                 // TODO someone add more stuff here
3515                 if(PRVM_clientedictfloat(edict, entnum)) // csqc networked
3516                         return true;
3517                 if(PRVM_clientedictfloat(edict, modelindex)) // visible ent?
3518                         return true;
3519                 if(PRVM_clientedictfloat(edict, effects)) // particle effect?
3520                         return true;
3521                 if(PRVM_clientedictfunction(edict, think)) // has a think function?
3522                         if(PRVM_clientedictfloat(edict, nextthink) > 0) // that actually will eventually run?
3523                                 return true;
3524                 if(*prvm_leaktest_ignore_classnames.string)
3525                 {
3526                         if(strstr(va(vabuf, sizeof(vabuf), " %s ", prvm_leaktest_ignore_classnames.string), va(vabuf2, sizeof(vabuf2), " %s ", PRVM_GetString(prog, PRVM_clientedictstring(edict, classname)))))
3527                                 return true;
3528                 }
3529         }
3530         else
3531         {
3532                 // menu prog does not have classnames
3533         }
3534         return false;
3535 }
3536
3537 static qbool PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, int mark)
3538 {
3539         int i, j;
3540         int edictnum = PRVM_NUM_FOR_EDICT(edict);
3541         const char *targetname = NULL;
3542
3543         if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer)
3544                 targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname));
3545
3546         if(targetname)
3547                 if(!*targetname) // ""
3548                         targetname = NULL;
3549
3550         for(j = 0; j < prog->num_edicts; ++j)
3551         {
3552                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3553                 if (ed->priv.required->mark < mark)
3554                         continue;
3555                 if(ed == edict)
3556                         continue;
3557                 if(targetname)
3558                 {
3559                         const char *target = PRVM_GetString(prog, PRVM_serveredictstring(ed, target));
3560                         if(target)
3561                                 if(!strcmp(target, targetname))
3562                                         return true;
3563                 }
3564                 for (i=0; i<prog->numfielddefs; ++i)
3565                 {
3566                         mdef_t *d = &prog->fielddefs[i];
3567                         if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3568                                 continue;
3569                         if(edictnum == PRVM_EDICTFIELDEDICT(ed, d->ofs))
3570                                 return true;
3571                 }
3572         }
3573
3574         return false;
3575 }
3576
3577 static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
3578 {
3579         int i, j;
3580         qbool found_new;
3581         int stage;
3582
3583         // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
3584         stage = 1;
3585         for(j = 0; j < prog->num_edicts; ++j)
3586         {
3587                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3588                 if(ed->free)
3589                         continue;
3590                 ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
3591         }
3592         for (i = 0;i < prog->numglobaldefs;i++)
3593         {
3594                 mdef_t *d = &prog->globaldefs[i];
3595                 prvm_edict_t *ed;
3596                 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
3597                         continue;
3598                 j = PRVM_GLOBALFIELDEDICT(d->ofs);
3599                 if (i < 0 || j >= prog->max_edicts) {
3600                         Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
3601                         continue;
3602                 }
3603                 ed = PRVM_EDICT_NUM(j);;
3604                 ed->priv.required->mark = stage;
3605         }
3606
3607         // Future stages: all entities that are referenced by an entity of the previous stage.
3608         do
3609         {
3610                 found_new = false;
3611                 for(j = 0; j < prog->num_edicts; ++j)
3612                 {
3613                         prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3614                         if(ed->free)
3615                                 continue;
3616                         if(ed->priv.required->mark)
3617                                 continue;
3618                         if(PRVM_IsEdictReferenced(prog, ed, stage))
3619                         {
3620                                 ed->priv.required->mark = stage + 1;
3621                                 found_new = true;
3622                         }
3623                 }
3624                 ++stage;
3625         }
3626         while(found_new);
3627         Con_DPrintf("leak check used %d stages to find all references\n", stage);
3628 }
3629
3630 void PRVM_LeakTest(prvm_prog_t *prog)
3631 {
3632         int i, j;
3633         qbool leaked = false;
3634
3635         if(!prog->leaktest_active)
3636                 return;
3637
3638         // 1. Strings
3639         for (i = 0; i < prog->numknownstrings; ++i)
3640         {
3641                 if(prog->knownstrings[i])
3642                 if(prog->knownstrings_flags[i])
3643                 if(prog->knownstrings_origin[i])
3644                 if(!PRVM_IsStringReferenced(prog, PRVM_KNOWNSTRINGBASE + i))
3645                 {
3646                         Con_Printf("Unreferenced string found!\n  Value: %s\n  Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
3647                         leaked = true;
3648                 }
3649         }
3650
3651         // 2. Edicts
3652         PRVM_MarkReferencedEdicts(prog);
3653         for(j = 0; j < prog->num_edicts; ++j)
3654         {
3655                 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
3656                 if(ed->free)
3657                         continue;
3658                 if(!ed->priv.required->mark)
3659                 if(ed->priv.required->allocation_origin)
3660                 {
3661                         Con_Printf("Unreferenced edict found!\n  Allocated at: %s\n", ed->priv.required->allocation_origin);
3662                         PRVM_ED_Print(prog, ed, NULL);
3663                         Con_Print("\n");
3664                         leaked = true;
3665                 }
3666
3667                 ed->priv.required->mark = 0; // clear marks again when done
3668         }
3669
3670         for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
3671         {
3672                 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
3673                 if(stringbuffer)
3674                 if(stringbuffer->origin)
3675                 {
3676                         Con_Printf("Open string buffer handle found!\n  Allocated at: %s\n", stringbuffer->origin);
3677                         leaked = true;
3678                 }
3679         }
3680
3681         for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
3682         {
3683                 if(prog->openfiles[i])
3684                 if(prog->openfiles_origin[i])
3685                 {
3686                         Con_Printf("Open file handle found!\n  Allocated at: %s\n", prog->openfiles_origin[i]);
3687                         leaked = true;
3688                 }
3689         }
3690
3691         for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
3692         {
3693                 if(prog->opensearches[i])
3694                 if(prog->opensearches_origin[i])
3695                 {
3696                         Con_Printf("Open search handle found!\n  Allocated at: %s\n", prog->opensearches_origin[i]);
3697                         leaked = true;
3698                 }
3699         }
3700
3701         if(!leaked)
3702                 Con_Printf("Congratulations. No leaks found.\n");
3703 }
3704
3705 void PRVM_GarbageCollection(prvm_prog_t *prog)
3706 {
3707         int limit = prvm_garbagecollection_scan_limit.integer;
3708         prvm_prog_garbagecollection_state_t *gc = &prog->gc;
3709         if (!prvm_garbagecollection_enable.integer)
3710                 return;
3711         // philosophy:
3712         // we like to limit how much scanning we do so it doesn't put a significant
3713         // burden on the cpu, so each of these are not complete scans, we also like
3714         // to have consistent cpu usage so we do a bit of work on each category of
3715         // leaked object every frame
3716         switch (gc->stage)
3717         {
3718         case PRVM_GC_START:
3719                 gc->stage++;
3720                 break;
3721         case PRVM_GC_GLOBALS_MARK:
3722                 for (; gc->globals_mark_progress < prog->numglobaldefs && (limit--) > 0; gc->globals_mark_progress++)
3723                 {
3724                         mdef_t *d = &prog->globaldefs[gc->globals_mark_progress];
3725                         switch (d->type)
3726                         {
3727                         case ev_string:
3728                                 {
3729                                         prvm_int_t s = prog->globals.ip[d->ofs];
3730                                         if (s & PRVM_KNOWNSTRINGBASE)
3731                                         {
3732                                                 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3733                                                 if (!prog->knownstrings[num])
3734                                                 {
3735                                                         // invalid
3736                                                         Con_DPrintf("PRVM_GarbageCollection: Found bogus strzone reference in global %i (global name: \"%s\"), erasing reference", d->ofs, PRVM_GetString(prog, d->s_name));
3737                                                         prog->globals.ip[d->ofs] = 0;
3738                                                         continue;
3739                                                 }
3740                                                 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3741                                         }
3742                                 }
3743                                 break;
3744                         default:
3745                                 break;
3746                         }
3747                 }
3748                 if (gc->globals_mark_progress >= prog->numglobaldefs)
3749                         gc->stage++;
3750                 break;
3751         case PRVM_GC_FIELDS_MARK:
3752                 for (; gc->fields_mark_progress < prog->numfielddefs && limit > 0;)
3753                 {
3754                         mdef_t *d = &prog->fielddefs[gc->fields_mark_progress];
3755                         switch (d->type)
3756                         {
3757                         case ev_string:
3758                                 //for (gc-> entityindex = 0; entityindex < prog->num_edicts; entityindex++)
3759                                 for (;gc->fields_mark_progress_entity < prog->num_edicts && (limit--) > 0;gc->fields_mark_progress_entity++)
3760                                 {
3761                                         int entityindex = gc->fields_mark_progress_entity;
3762                                         prvm_int_t s = prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs];
3763                                         if (s & PRVM_KNOWNSTRINGBASE)
3764                                         {
3765                                                 prvm_int_t num = s - PRVM_KNOWNSTRINGBASE;
3766                                                 if (!prog->knownstrings[num])
3767                                                 {
3768                                                         // invalid
3769                                                         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));
3770                                                         prog->edictsfields.ip[entityindex * prog->entityfields + d->ofs] = 0;
3771                                                         continue;
3772                                                 }
3773                                                 prog->knownstrings_flags[num] = (prog->knownstrings_flags[num] | KNOWNSTRINGFLAG_GCMARK) & ~KNOWNSTRINGFLAG_GCPRUNE;
3774                                         }
3775                                 }
3776                                 if (gc->fields_mark_progress_entity >= prog->num_edicts)
3777                                 {
3778                                         gc->fields_mark_progress_entity = 0;
3779                                         gc->fields_mark_progress++;
3780                                 }
3781                                 break;
3782                         default:
3783                                 gc->fields_mark_progress_entity = 0;
3784                                 gc->fields_mark_progress++;
3785                                 break;
3786                         }
3787                 }
3788                 if (gc->fields_mark_progress >= prog->numfielddefs)
3789                         gc->stage++;
3790                 break;
3791         case PRVM_GC_KNOWNSTRINGS_SWEEP:
3792                 // free any strzone'd strings that are not marked
3793                 if (!prvm_garbagecollection_strings.integer)
3794                 {
3795                         gc->stage++;
3796                         break;
3797                 }
3798                 for (;gc->knownstrings_sweep_progress < prog->numknownstrings && (limit--) > 0;gc->knownstrings_sweep_progress++)
3799                 {
3800                         int num = gc->knownstrings_sweep_progress;
3801                         if (prog->knownstrings[num] && (prog->knownstrings_flags[num] & (KNOWNSTRINGFLAG_GCMARK | KNOWNSTRINGFLAG_ENGINE)) == 0)
3802                         {
3803                                 if (prog->knownstrings_flags[num] & KNOWNSTRINGFLAG_GCPRUNE)
3804                                 {
3805                                         // string has been marked for pruning two passes in a row
3806                                         if (prvm_garbagecollection_notify.integer)
3807                                                 Con_DPrintf("prvm_garbagecollection_notify: %s: freeing unreferenced string %i: \"%s\"\n", prog->name, num, prog->knownstrings[num]);
3808                                         Mem_Free((char *)prog->knownstrings[num]);
3809                                         prog->knownstrings[num] = NULL;
3810                                         prog->knownstrings_flags[num] = 0;
3811                                         prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
3812                                 }
3813                                 else
3814                                 {
3815                                         // mark it for pruning next pass
3816                                         prog->knownstrings_flags[num] |= KNOWNSTRINGFLAG_GCPRUNE;
3817                                 }
3818                         }
3819                 }
3820                 if (gc->knownstrings_sweep_progress >= prog->numknownstrings)
3821                         gc->stage++;
3822                 break;
3823         case PRVM_GC_RESET:
3824         default:
3825                 memset(gc, 0, sizeof(*gc));
3826         }
3827 }