]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_edict.c
Merge PR 'Use the text from modinfo.txt as the mod menu entry'
[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 (set developer >= 1 to see these)"};
50 /// At 50k in Xonotic with 8 bots scans take about: 24s server, 25s menu, 9s client.
51 /// At 50k in Quake 1.5: 2.2s server, 0.14s client.
52 /// At 50k impact on high FPS benchmarks is negligible, at 100k impact is low but measurable.
53 cvar_t prvm_garbagecollection_scan_limit = {CF_CLIENT | CF_SERVER, "prvm_garbagecollection_scan_limit", "50000", "scan this many fields or resources per second to free up unreferenced resources"};
54 cvar_t prvm_garbagecollection_strings = {CF_CLIENT | CF_SERVER, "prvm_garbagecollection_strings", "1", "automatically call strunzone() on strings that are not referenced"};
55 cvar_t prvm_stringdebug = {CF_CLIENT | CF_SERVER, "prvm_stringdebug", "0", "Print debug and warning messages related to strings"};
56
57 static double prvm_reuseedicts_always_allow = 0;
58 qbool prvm_runawaycheck = true;
59
60 //============================================================================
61 // mempool handling
62
63 /*
64 ===============
65 PRVM_MEM_Alloc
66 ===============
67 */
68 static void PRVM_MEM_Alloc(prvm_prog_t *prog)
69 {
70         int i;
71
72         // reserve space for the null entity aka world
73         // check bound of max_edicts
74         prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
75         prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
76
77         // edictprivate_size has to be min as big prvm_edict_private_t
78         prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
79
80         // alloc edicts
81         prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
82
83         // alloc edict private space
84         prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
85
86         // alloc edict fields
87         prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
88         prog->edictsfields.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, prog->entityfieldsarea * sizeof(prvm_vec_t));
89
90         // set edict pointers
91         for(i = 0; i < prog->max_edicts; i++)
92         {
93                 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
94                 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
95         }
96 }
97
98 /*
99 ===============
100 PRVM_MEM_IncreaseEdicts
101 ===============
102 */
103 void PRVM_MEM_IncreaseEdicts(prvm_prog_t *prog)
104 {
105         int             i;
106
107         if(prog->max_edicts >= prog->limit_edicts)
108                 return;
109
110         prog->begin_increase_edicts(prog);
111
112         // increase edicts
113         prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
114
115         prog->entityfieldsarea = prog->entityfields * prog->max_edicts;
116         prog->edictsfields.fp = (prvm_vec_t*)Mem_Realloc(prog->progs_mempool, (void *)prog->edictsfields.fp, prog->entityfieldsarea * sizeof(prvm_vec_t));
117         prog->edictprivate = (void *)Mem_Realloc(prog->progs_mempool, (void *)prog->edictprivate, prog->max_edicts * prog->edictprivate_size);
118
119         //set e and v pointers
120         for(i = 0; i < prog->max_edicts; i++)
121         {
122                 prog->edicts[i].priv.required  = (prvm_edict_private_t *)((unsigned char  *)prog->edictprivate + i * prog->edictprivate_size);
123                 prog->edicts[i].fields.fp = prog->edictsfields.fp + i * prog->entityfields;
124         }
125
126         prog->end_increase_edicts(prog);
127 }
128
129 //============================================================================
130 // normal prvm
131
132 int PRVM_ED_FindFieldOffset(prvm_prog_t *prog, const char *field)
133 {
134         mdef_t *d;
135         d = PRVM_ED_FindField(prog, field);
136         if (!d)
137                 return -1;
138         return d->ofs;
139 }
140
141 int PRVM_ED_FindGlobalOffset(prvm_prog_t *prog, const char *global)
142 {
143         mdef_t *d;
144         d = PRVM_ED_FindGlobal(prog, global);
145         if (!d)
146                 return -1;
147         return d->ofs;
148 }
149
150 func_t PRVM_ED_FindFunctionOffset(prvm_prog_t *prog, const char *function)
151 {
152         mfunction_t *f;
153         f = PRVM_ED_FindFunction(prog, function);
154         if (!f)
155                 return 0;
156         return (func_t)(f - prog->functions);
157 }
158
159 /*
160 =================
161 PRVM_ProgFromString
162 =================
163 */
164 prvm_prog_t *PRVM_ProgFromString(const char *str)
165 {
166         if (!strcmp(str, "server"))
167                 return SVVM_prog;
168         if (!strcmp(str, "client"))
169                 return CLVM_prog;
170 #ifdef CONFIG_MENU
171         if (!strcmp(str, "menu"))
172                 return MVM_prog;
173 #endif
174         return NULL;
175 }
176
177 /*
178 =================
179 PRVM_FriendlyProgFromString
180 =================
181 */
182 prvm_prog_t *PRVM_FriendlyProgFromString(const char *str)
183 {
184         prvm_prog_t *prog = PRVM_ProgFromString(str);
185         if (!prog)
186         {
187                 Con_Printf("%s: unknown program name\n", str);
188                 return NULL;
189         }
190         if (!prog->loaded)
191         {
192                 Con_Printf("%s: program is not loaded\n", str);
193                 return NULL;
194         }
195         return prog;
196 }
197
198 /*
199 =================
200 PRVM_ED_ClearEdict
201
202 Sets everything to NULL.
203
204 Nota bene: this also marks the entity as allocated if it has been previously
205 freed and sets the allocation origin.
206 =================
207 */
208 void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e)
209 {
210         memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
211         e->free = false;
212         e->freetime = host.realtime;
213         if(e->priv.required->allocation_origin)
214                 Mem_Free((char *)e->priv.required->allocation_origin);
215         e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
216
217         // AK: Let the init_edict function determine if something needs to be initialized
218         prog->init_edict(prog, e);
219 }
220
221 const char *PRVM_AllocationOrigin(prvm_prog_t *prog)
222 {
223         char *buf = NULL;
224         if(prog->leaktest_active)
225         if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
226         {
227                 // bones_was_here: this is the smallest 64 multiple that avoids truncation in Xonotic (was 256)
228                 buf = (char *)PRVM_Alloc(448);
229                 PRVM_ShortStackTrace(prog, buf, 448);
230         }
231         return buf;
232 }
233
234 /*
235 =================
236 PRVM_ED_CanAlloc
237
238 Returns if this particular edict could get allocated by PRVM_ED_Alloc
239 =================
240 */
241 qbool PRVM_ED_CanAlloc(prvm_prog_t *prog, prvm_edict_t *e)
242 {
243         if(!e->free)
244                 return false;
245         if(prvm_reuseedicts_always_allow == host.realtime)
246                 return true;
247         if(host.realtime <= e->freetime + 0.1 && prvm_reuseedicts_neverinsameframe.integer)
248                 return false; // never allow reuse in same frame (causes networking trouble)
249         if(e->freetime < prog->starttime + prvm_reuseedicts_startuptime.value)
250                 return true;
251         if(host.realtime > e->freetime + 1)
252                 return true;
253         return false; // entity slot still blocked because the entity was freed less than one second ago
254 }
255
256 /*
257 =================
258 PRVM_ED_Alloc
259
260 Either finds a free edict, or allocates a new one.
261 Try to avoid reusing an entity that was recently freed, because it
262 can cause the client to think the entity morphed into something else
263 instead of being removed and recreated, which can cause interpolated
264 angles and bad trails.
265 =================
266 */
267 prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog)
268 {
269         int i;
270         prvm_edict_t *e;
271
272         // the client qc dont need maxclients
273         // thus it doesnt need to use svs.maxclients
274         // AK:  changed i=svs.maxclients+1
275         // AK:  changed so the edict 0 wont spawn -> used as reserved/world entity
276         //              although the menu/client has no world
277         for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
278         {
279                 e = PRVM_EDICT_NUM(i);
280                 if(PRVM_ED_CanAlloc(prog, e))
281                 {
282                         PRVM_ED_ClearEdict (prog, e);
283                         return e;
284                 }
285         }
286
287         if (i == prog->limit_edicts)
288                 prog->error_cmd("%s: PRVM_ED_Alloc: no free edicts", prog->name);
289
290         prog->num_edicts++;
291         if (prog->num_edicts >= prog->max_edicts)
292                 PRVM_MEM_IncreaseEdicts(prog);
293
294         e = PRVM_EDICT_NUM(i);
295
296         PRVM_ED_ClearEdict(prog, e);
297         return e;
298 }
299
300 /*
301 =================
302 PRVM_ED_Free
303
304 Marks the edict as free
305
306 FIXME: walk all entities and NULL out references to this entity
307 bones_was_here: do not want, that would break chains immediately!
308 Currently chains aren't broken by removing an entity, at least with prvm_reuseedicts_neverinsameframe 1
309 which is very handy and some QC code will depend on it.
310 =================
311 */
312 void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
313 {
314         // dont delete the null entity (world) or reserved edicts
315         if (ed - prog->edicts <= prog->reserved_edicts)
316                 return;
317
318         prog->free_edict(prog, ed);
319
320         ed->free = true;
321         ed->freetime = host.realtime;
322         if(ed->priv.required->allocation_origin)
323         {
324                 Mem_Free((char *)ed->priv.required->allocation_origin);
325                 ed->priv.required->allocation_origin = NULL;
326         }
327 }
328
329 //===========================================================================
330
331 /*
332 ============
333 PRVM_ED_GlobalAtOfs
334 ============
335 */
336 static mdef_t *PRVM_ED_GlobalAtOfs (prvm_prog_t *prog, unsigned int ofs)
337 {
338         mdef_t          *def;
339         int                     i;
340
341         for (i = 0;i < prog->numglobaldefs;i++)
342         {
343                 def = &prog->globaldefs[i];
344                 if (def->ofs == ofs)
345                         return def;
346         }
347         return NULL;
348 }
349
350 /*
351 ============
352 PRVM_ED_FieldAtOfs
353 ============
354 */
355 mdef_t *PRVM_ED_FieldAtOfs (prvm_prog_t *prog, unsigned int ofs)
356 {
357         mdef_t          *def;
358         int                     i;
359
360         for (i = 0;i < prog->numfielddefs;i++)
361         {
362                 def = &prog->fielddefs[i];
363                 if (def->ofs == ofs)
364                         return def;
365         }
366         return NULL;
367 }
368
369 /*
370 ============
371 PRVM_ED_FindField
372 ============
373 */
374 mdef_t *PRVM_ED_FindField (prvm_prog_t *prog, const char *name)
375 {
376         mdef_t *def;
377         int i;
378
379         for (i = 0;i < prog->numfielddefs;i++)
380         {
381                 def = &prog->fielddefs[i];
382                 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
383                         return def;
384         }
385         return NULL;
386 }
387
388 /*
389 ============
390 PRVM_ED_FindGlobal
391 ============
392 */
393 mdef_t *PRVM_ED_FindGlobal (prvm_prog_t *prog, const char *name)
394 {
395         mdef_t *def;
396         int i;
397
398         for (i = 0;i < prog->numglobaldefs;i++)
399         {
400                 def = &prog->globaldefs[i];
401                 if (!strcmp(PRVM_GetString(prog, def->s_name), name))
402                         return def;
403         }
404         return NULL;
405 }
406
407 /*
408 ============
409 PRVM_ED_FindGlobalEval
410 ============
411 */
412 prvm_eval_t *PRVM_ED_FindGlobalEval(prvm_prog_t *prog, const char *name)
413 {
414         mdef_t *def = PRVM_ED_FindGlobal(prog, name);
415         return def ? (prvm_eval_t *) &prog->globals.fp[def->ofs] : NULL;
416 }
417
418 /*
419 ============
420 PRVM_ED_FindFunction
421 ============
422 */
423 mfunction_t *PRVM_ED_FindFunction (prvm_prog_t *prog, const char *name)
424 {
425         mfunction_t             *func;
426         int                             i;
427
428         for (i = 0;i < prog->numfunctions;i++)
429         {
430                 func = &prog->functions[i];
431                 if (!strcmp(PRVM_GetString(prog, func->s_name), name))
432                         return func;
433         }
434         return NULL;
435 }
436
437
438 /*
439 ============
440 PRVM_ValueString
441
442 Returns a string describing *data in a type specific manner
443 =============
444 */
445 static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
446 {
447         mdef_t *def;
448         mfunction_t *f;
449         int n;
450
451         type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
452
453         switch (type)
454         {
455         case ev_string:
456                 dp_strlcpy (line, PRVM_GetString (prog, val->string), linelength);
457                 break;
458         case ev_entity:
459                 n = val->edict;
460                 if (n < 0 || n >= prog->max_edicts)
461                         dpsnprintf (line, linelength, "entity %i (invalid!)", n);
462                 else
463                         dpsnprintf (line, linelength, "entity %i", n);
464                 break;
465         case ev_function:
466                 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
467                 {
468                         f = prog->functions + val->function;
469                         dpsnprintf (line, linelength, "%s()", PRVM_GetString(prog, f->s_name));
470                 }
471                 else
472                         dpsnprintf (line, linelength, "function %" PRVM_PRIi "() (invalid!)", val->function);
473                 break;
474         case ev_field:
475                 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
476                 if (def != NULL)
477                         dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
478                 else
479                         dpsnprintf (line, linelength, "field %" PRVM_PRIi " (invalid!)", val->_int );
480                 break;
481         case ev_void:
482                 dpsnprintf (line, linelength, "void");
483                 break;
484         case ev_float:
485                 // LadyHavoc: changed from %5.1f to %10.4f
486                 dpsnprintf (line, linelength, PRVM_FLOAT_LOSSLESS_FORMAT, val->_float);
487                 break;
488         case ev_vector:
489                 // LadyHavoc: changed from %5.1f to %10.4f
490                 dpsnprintf (line, linelength, "'" PRVM_VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]);
491                 break;
492         case ev_pointer:
493                 dpsnprintf (line, linelength, "pointer");
494                 break;
495         default:
496                 dpsnprintf (line, linelength, "bad type %i", (int) type);
497                 break;
498         }
499
500         return line;
501 }
502
503 /*
504 ============
505 PRVM_UglyValueString
506
507 Returns a string describing *data in a type specific manner
508 Easier to parse than PR_ValueString
509 =============
510 */
511 char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, char *line, size_t linelength)
512 {
513         int i;
514         const char *s;
515         mdef_t *def;
516         mfunction_t *f;
517
518         type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
519
520         switch (type)
521         {
522         case ev_string:
523                 // Parse the string a bit to turn special characters
524                 // (like newline, specifically) into escape codes,
525                 // this fixes saving games from various mods
526                 s = PRVM_GetString (prog, val->string);
527                 for (i = 0;i < (int)linelength - 2 && *s;)
528                 {
529                         if (*s == '\n')
530                         {
531                                 line[i++] = '\\';
532                                 line[i++] = 'n';
533                         }
534                         else if (*s == '\r')
535                         {
536                                 line[i++] = '\\';
537                                 line[i++] = 'r';
538                         }
539                         else if (*s == '\\')
540                         {
541                                 line[i++] = '\\';
542                                 line[i++] = '\\';
543                         }
544                         else if (*s == '"')
545                         {
546                                 line[i++] = '\\';
547                                 line[i++] = '"';
548                         }
549                         else
550                                 line[i++] = *s;
551                         s++;
552                 }
553                 line[i] = '\0';
554                 break;
555         case ev_entity:
556                 i = val->edict;
557                 dpsnprintf (line, linelength, "%i", i);
558                 break;
559         case ev_function:
560                 if ((unsigned int)val->function < (unsigned int)prog->progs_numfunctions)
561                 {
562                         f = prog->functions + val->function;
563                         dp_strlcpy (line, PRVM_GetString (prog, f->s_name), linelength);
564                 }
565                 else
566                         dpsnprintf (line, linelength, "bad function %" PRVM_PRIi " (invalid!)", val->function);
567                 break;
568         case ev_field:
569                 def = PRVM_ED_FieldAtOfs ( prog, val->_int );
570                 if (def != NULL)
571                         dpsnprintf (line, linelength, ".%s", PRVM_GetString(prog, def->s_name));
572                 else
573                         dpsnprintf (line, linelength, "field %" PRVM_PRIi "(invalid!)", val->_int );
574                 break;
575         case ev_void:
576                 dpsnprintf (line, linelength, "void");
577                 break;
578         case ev_float:
579                 dpsnprintf (line, linelength, PRVM_FLOAT_LOSSLESS_FORMAT, val->_float);
580                 break;
581         case ev_vector:
582                 dpsnprintf (line, linelength, PRVM_VECTOR_LOSSLESS_FORMAT, val->vector[0], val->vector[1], val->vector[2]);
583                 break;
584         default:
585                 dpsnprintf (line, linelength, "bad type %i", type);
586                 break;
587         }
588
589         return line;
590 }
591
592 /*
593 ============
594 PRVM_GlobalString
595
596 Returns a string with a description and the contents of a global,
597 padded to 20 field width
598 ============
599 */
600 char *PRVM_GlobalString (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
601 {
602         char    *s;
603         //size_t        i;
604         mdef_t  *def;
605         prvm_eval_t     *val;
606         char valuebuf[MAX_INPUTLINE];
607
608         val = (prvm_eval_t *)&prog->globals.fp[ofs];
609         def = PRVM_ED_GlobalAtOfs(prog, ofs);
610         if (!def)
611                 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
612         else
613         {
614                 s = PRVM_ValueString (prog, (etype_t)def->type, val, valuebuf, sizeof(valuebuf));
615                 dpsnprintf (line, linelength, "%s (=%s)", PRVM_GetString(prog, def->s_name), s);
616         }
617
618         //i = strlen(line);
619         //for ( ; i<20 ; i++)
620         //      strcat (line," ");
621         //strcat (line," ");
622
623         return line;
624 }
625
626 char *PRVM_GlobalStringNoContents (prvm_prog_t *prog, int ofs, char *line, size_t linelength)
627 {
628         //size_t        i;
629         mdef_t  *def;
630
631         def = PRVM_ED_GlobalAtOfs(prog, ofs);
632         if (!def)
633                 dpsnprintf (line, linelength, "GLOBAL%i", ofs);
634         else
635                 dpsnprintf (line, linelength, "%s", PRVM_GetString(prog, def->s_name));
636
637         //i = strlen(line);
638         //for ( ; i<20 ; i++)
639         //      strcat (line," ");
640         //strcat (line," ");
641
642         return line;
643 }
644
645
646 /*
647 =============
648 PRVM_ED_Print
649
650 For debugging
651 =============
652 */
653 // LadyHavoc: optimized this to print out much more quickly (tempstring)
654 // LadyHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
655 void PRVM_ED_Print(prvm_prog_t *prog, prvm_edict_t *ed, const char *wildcard_fieldname)
656 {
657         size_t  l;
658         mdef_t  *d;
659         prvm_eval_t     *val;
660         int             i, j;
661         const char      *name;
662         int             type;
663         char    tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
664         char    valuebuf[MAX_INPUTLINE];
665
666         if (ed->free)
667         {
668                 Con_Printf("%s: FREE\n",prog->name);
669                 return;
670         }
671
672         tempstring[0] = 0;
673         dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", prog->name, PRVM_NUM_FOR_EDICT(ed));
674         for (i = 1;i < prog->numfielddefs;i++)
675         {
676                 d = &prog->fielddefs[i];
677                 name = PRVM_GetString(prog, d->s_name);
678                 if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
679                         continue;       // skip _x, _y, _z vars
680
681                 // Check Field Name Wildcard
682                 if(wildcard_fieldname)
683                         if( !matchpattern(name, wildcard_fieldname, 1) )
684                                 // Didn't match; skip
685                                 continue;
686
687                 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
688
689         // if the value is still all 0, skip the field
690                 type = d->type & ~DEF_SAVEGLOBAL;
691
692                 for (j=0 ; j<prvm_type_size[type] ; j++)
693                         if (val->ivector[j])
694                                 break;
695                 if (j == prvm_type_size[type])
696                         continue;
697
698                 if (strlen(name) > sizeof(tempstring2)-4)
699                 {
700                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
701                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
702                         tempstring2[sizeof(tempstring2)-1] = 0;
703                         name = tempstring2;
704                 }
705                 dp_strlcat(tempstring, name, sizeof(tempstring));
706                 for (l = strlen(name);l < 14;l++)
707                         dp_strlcat(tempstring, " ", sizeof(tempstring));
708                 dp_strlcat(tempstring, " ", sizeof(tempstring));
709
710                 name = PRVM_ValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf));
711                 if (strlen(name) > sizeof(tempstring2)-4)
712                 {
713                         memcpy (tempstring2, name, sizeof(tempstring2)-4);
714                         tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
715                         tempstring2[sizeof(tempstring2)-1] = 0;
716                         name = tempstring2;
717                 }
718                 dp_strlcat(tempstring, name, sizeof(tempstring));
719                 dp_strlcat(tempstring, "\n", sizeof(tempstring));
720                 if (strlen(tempstring) >= sizeof(tempstring)/2)
721                 {
722                         Con_Print(tempstring);
723                         tempstring[0] = 0;
724                 }
725         }
726         if (tempstring[0])
727                 Con_Print(tempstring);
728 }
729
730 /*
731 =============
732 PRVM_ED_Write
733
734 For savegames
735 =============
736 */
737 void PRVM_ED_Write (prvm_prog_t *prog, qfile_t *f, prvm_edict_t *ed)
738 {
739         mdef_t  *d;
740         prvm_eval_t     *val;
741         int             i, j;
742         const char      *name;
743         int             type;
744         char vabuf[1024];
745         char valuebuf[MAX_INPUTLINE];
746
747         FS_Print(f, "{\n");
748
749         if (ed->free)
750         {
751                 FS_Print(f, "}\n");
752                 return;
753         }
754
755         for (i = 1;i < prog->numfielddefs;i++)
756         {
757                 d = &prog->fielddefs[i];
758                 name = PRVM_GetString(prog, d->s_name);
759
760                 if(developer_entityparsing.integer)
761                         Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
762
763                 //if(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
764                 if(strlen(name) > 1 && name[strlen(name)-2] == '_')
765                         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?)
766
767                 val = (prvm_eval_t *)(ed->fields.fp + d->ofs);
768
769         // if the value is still all 0, skip the field
770                 type = d->type & ~DEF_SAVEGLOBAL;
771                 for (j=0 ; j<prvm_type_size[type] ; j++)
772                         if (val->ivector[j])
773                                 break;
774                 if (j == prvm_type_size[type])
775                         continue;
776
777                 FS_Printf(f,"\"%s\" ",name);
778                 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_Write, ent=%d, name=%s", i, name);
779                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)d->type, val, valuebuf, sizeof(valuebuf)));
780                 prog->statestring = NULL;
781         }
782
783         FS_Print(f, "}\n");
784 }
785
786 void PRVM_ED_PrintNum (prvm_prog_t *prog, int ent, const char *wildcard_fieldname)
787 {
788         PRVM_ED_Print(prog, PRVM_EDICT_NUM(ent), wildcard_fieldname);
789 }
790
791 /*
792 =============
793 PRVM_ED_PrintEdicts_f
794
795 For debugging, prints all the entities in the current server
796 =============
797 */
798 void PRVM_ED_PrintEdicts_f(cmd_state_t *cmd)
799 {
800         prvm_prog_t *prog;
801         int             i;
802         const char *wildcard_fieldname;
803
804         if(Cmd_Argc(cmd) < 2 || Cmd_Argc(cmd) > 3)
805         {
806                 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
807                 return;
808         }
809
810         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
811                 return;
812
813         if( Cmd_Argc(cmd) == 3)
814                 wildcard_fieldname = Cmd_Argv(cmd, 2);
815         else
816                 wildcard_fieldname = NULL;
817
818         Con_Printf("%s: %i entities\n", prog->name, prog->num_edicts);
819         for (i=0 ; i<prog->num_edicts ; i++)
820                 PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
821 }
822
823 /*
824 =============
825 PRVM_ED_PrintEdict_f
826
827 For debugging, prints a single edict
828 =============
829 */
830 static void PRVM_ED_PrintEdict_f(cmd_state_t *cmd)
831 {
832         prvm_prog_t *prog;
833         int             i;
834         const char      *wildcard_fieldname;
835
836         if(Cmd_Argc(cmd) < 3 || Cmd_Argc(cmd) > 4)
837         {
838                 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
839                 return;
840         }
841
842         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
843                 return;
844
845         i = atoi (Cmd_Argv(cmd, 2));
846         if (i >= prog->num_edicts)
847         {
848                 Con_Print("Bad edict number\n");
849                 return;
850         }
851         if( Cmd_Argc(cmd) == 4)
852                 // Optional Wildcard Provided
853                 wildcard_fieldname = Cmd_Argv(cmd, 3);
854         else
855                 // Use All
856                 wildcard_fieldname = NULL;
857         PRVM_ED_PrintNum (prog, i, wildcard_fieldname);
858 }
859
860 /*
861 =============
862 PRVM_ED_Count
863
864 For debugging
865 =============
866 */
867 // 2 possibilities : 1. just displaying the active edict count
868 //                                       2. making a function pointer [x]
869 static void PRVM_ED_Count_f(cmd_state_t *cmd)
870 {
871         prvm_prog_t *prog;
872
873         if(Cmd_Argc(cmd) != 2)
874         {
875                 Con_Print("prvm_count <program name>\n");
876                 return;
877         }
878
879         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
880                 return;
881
882         prog->count_edicts(prog);
883 }
884
885 /*
886 ==============================================================================
887
888                                         ARCHIVING GLOBALS
889
890 FIXME: need to tag constants, doesn't really work
891 ==============================================================================
892 */
893
894 /*
895 =============
896 PRVM_ED_WriteGlobals
897 =============
898 */
899 void PRVM_ED_WriteGlobals (prvm_prog_t *prog, qfile_t *f)
900 {
901         mdef_t          *def;
902         int                     i;
903         const char              *name;
904         int                     type;
905         char vabuf[1024];
906         char valuebuf[MAX_INPUTLINE];
907
908         FS_Print(f,"{\n");
909         for (i = 0;i < prog->numglobaldefs;i++)
910         {
911                 def = &prog->globaldefs[i];
912                 type = def->type;
913                 if ( !(def->type & DEF_SAVEGLOBAL) )
914                         continue;
915                 type &= ~DEF_SAVEGLOBAL;
916
917                 if (type != ev_string && type != ev_float && type != ev_entity)
918                         continue;
919
920                 name = PRVM_GetString(prog, def->s_name);
921
922                 if(developer_entityparsing.integer)
923                         Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
924
925                 prog->statestring = va(vabuf, sizeof(vabuf), "PRVM_ED_WriteGlobals, name=%s", name);
926                 FS_Printf(f,"\"%s\" ", name);
927                 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString(prog, (etype_t)type, (prvm_eval_t *)&prog->globals.fp[def->ofs], valuebuf, sizeof(valuebuf)));
928                 prog->statestring = NULL;
929         }
930         FS_Print(f,"}\n");
931 }
932
933 /*
934 =============
935 PRVM_ED_ParseGlobals
936 =============
937 */
938 void PRVM_ED_ParseGlobals (prvm_prog_t *prog, const char *data)
939 {
940         char keyname[MAX_INPUTLINE];
941         mdef_t *key;
942
943         while (1)
944         {
945                 // parse key
946                 if (!COM_ParseToken_Simple(&data, false, false, true))
947                         prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
948                 if (com_token[0] == '}')
949                         break;
950
951                 if (developer_entityparsing.integer)
952                         Con_Printf("Key: \"%s\"", com_token);
953
954                 dp_strlcpy (keyname, com_token, sizeof(keyname));
955
956                 // parse value
957                 if (!COM_ParseToken_Simple(&data, false, true, true))
958                         prog->error_cmd("PRVM_ED_ParseGlobals: EOF without closing brace");
959
960                 if (developer_entityparsing.integer)
961                         Con_Printf(" \"%s\"\n", com_token);
962
963                 if (com_token[0] == '}')
964                         prog->error_cmd("PRVM_ED_ParseGlobals: closing brace without data");
965
966                 key = PRVM_ED_FindGlobal (prog, keyname);
967                 if (!key)
968                 {
969                         Con_DPrintf("'%s' is not a global on %s\n", keyname, prog->name);
970                         continue;
971                 }
972
973                 if (!PRVM_ED_ParseEpair(prog, NULL, key, com_token, true))
974                         prog->error_cmd("PRVM_ED_ParseGlobals: parse error");
975         }
976 }
977
978 //============================================================================
979
980
981 /*
982 =============
983 PRVM_ED_ParseEval
984
985 Can parse either fields or globals
986 returns false if error
987 =============
988 */
989 qbool PRVM_ED_ParseEpair(prvm_prog_t *prog, prvm_edict_t *ent, mdef_t *key, const char *s, qbool parsebackslash)
990 {
991         int i, l;
992         char *new_p;
993         mdef_t *def;
994         prvm_eval_t *val;
995         mfunction_t *func;
996
997         if (ent)
998                 val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
999         else
1000                 val = (prvm_eval_t *)(prog->globals.fp + key->ofs);
1001         switch (key->type & ~DEF_SAVEGLOBAL)
1002         {
1003         case ev_string:
1004                 l = (int)strlen(s) + 1;
1005                 val->string = PRVM_AllocString(prog, l, &new_p);
1006                 for (i = 0;i < l;i++)
1007                 {
1008                         if (s[i] == '\\' && s[i+1] && parsebackslash)
1009                         {
1010                                 i++;
1011                                 if (s[i] == 'n')
1012                                         *new_p++ = '\n';
1013                                 else if (s[i] == 'r')
1014                                         *new_p++ = '\r';
1015                                 else
1016                                         *new_p++ = s[i];
1017                         }
1018                         else
1019                                 *new_p++ = s[i];
1020                 }
1021                 break;
1022
1023         case ev_float:
1024                 while (*s && ISWHITESPACE(*s))
1025                         s++;
1026                 val->_float = atof(s);
1027                 break;
1028
1029         case ev_vector:
1030                 for (i = 0;i < 3;i++)
1031                 {
1032                         while (*s && ISWHITESPACE(*s))
1033                                 s++;
1034                         if (!*s)
1035                                 break;
1036                         val->vector[i] = atof(s);
1037                         while (!ISWHITESPACE(*s))
1038                                 s++;
1039                         if (!*s)
1040                                 break;
1041                 }
1042                 break;
1043
1044         case ev_entity:
1045                 while (*s && ISWHITESPACE(*s))
1046                         s++;
1047                 i = atoi(s);
1048                 if (i >= prog->limit_edicts)
1049                         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);
1050                 while (i >= prog->max_edicts)
1051                         PRVM_MEM_IncreaseEdicts(prog);
1052                 // if IncreaseEdicts was called the base pointer needs to be updated
1053                 if (ent)
1054                         val = (prvm_eval_t *)(ent->fields.fp + key->ofs);
1055                 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1056                 break;
1057
1058         case ev_field:
1059                 if (*s != '.')
1060                 {
1061                         Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, prog->name);
1062                         return false;
1063                 }
1064                 def = PRVM_ED_FindField(prog, s + 1);
1065                 if (!def)
1066                 {
1067                         Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, prog->name);
1068                         return false;
1069                 }
1070                 val->_int = def->ofs;
1071                 break;
1072
1073         case ev_function:
1074                 func = PRVM_ED_FindFunction(prog, s);
1075                 if (!func)
1076                 {
1077                         Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, prog->name);
1078                         return false;
1079                 }
1080                 val->function = func - prog->functions;
1081                 break;
1082
1083         default:
1084                 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);
1085                 return false;
1086         }
1087         return true;
1088 }
1089
1090 /*
1091 =============
1092 PRVM_GameCommand_f
1093
1094 Console command to send a string to QC function GameCommand of the
1095 indicated progs
1096
1097 Usage:
1098   sv_cmd adminmsg 3 "do not teamkill"
1099   cl_cmd someclientcommand
1100   menu_cmd somemenucommand
1101
1102 All progs can support this extension; sg calls it in server QC, cg in client
1103 QC, mg in menu QC.
1104 =============
1105 */
1106 static void PRVM_GameCommand(cmd_state_t *cmd, const char *whichprogs, const char *whichcmd)
1107 {
1108         prvm_prog_t *prog;
1109         if(Cmd_Argc(cmd) < 1)
1110         {
1111                 Con_Printf("%s text...\n", whichcmd);
1112                 return;
1113         }
1114
1115         if (!(prog = PRVM_FriendlyProgFromString(whichprogs)))
1116                 return;
1117
1118         if(!PRVM_allfunction(GameCommand))
1119         {
1120                 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1121         }
1122         else
1123         {
1124                 int restorevm_tempstringsbuf_cursize;
1125                 const char *s;
1126
1127                 s = Cmd_Args(cmd);
1128
1129                 restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1130                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(prog, s ? s : "", s ? strlen(s) : 0);
1131                 prog->ExecuteProgram(prog, PRVM_allfunction(GameCommand), "QC function GameCommand is missing");
1132                 prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1133         }
1134 }
1135 static void PRVM_GameCommand_Server_f(cmd_state_t *cmd)
1136 {
1137         PRVM_GameCommand(cmd, "server", "sv_cmd");
1138 }
1139 static void PRVM_GameCommand_Client_f(cmd_state_t *cmd)
1140 {
1141         PRVM_GameCommand(cmd, "client", "cl_cmd");
1142 }
1143 static void PRVM_GameCommand_Menu_f(cmd_state_t *cmd)
1144 {
1145         PRVM_GameCommand(cmd, "menu", "menu_cmd");
1146 }
1147
1148 /*
1149 =============
1150 PRVM_ED_EdictGet_f
1151
1152 Console command to load a field of a specified edict
1153 =============
1154 */
1155 static void PRVM_ED_EdictGet_f(cmd_state_t *cmd)
1156 {
1157         prvm_prog_t *prog;
1158         prvm_edict_t *ed;
1159         mdef_t *key;
1160         const char *s;
1161         prvm_eval_t *v;
1162         char valuebuf[MAX_INPUTLINE];
1163
1164         if(Cmd_Argc(cmd) != 4 && Cmd_Argc(cmd) != 5)
1165         {
1166                 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1167                 return;
1168         }
1169
1170         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1171                 return;
1172
1173         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1174
1175         if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1176         {
1177                 Con_Printf("Key %s not found !\n", Cmd_Argv(cmd, 3));
1178                 goto fail;
1179         }
1180
1181         v = (prvm_eval_t *)(ed->fields.fp + key->ofs);
1182         s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1183         if(Cmd_Argc(cmd) == 5)
1184         {
1185                 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 4), cmd->cvars_flagsmask);
1186                 if (cvar)
1187                         if(Cvar_Readonly(cvar, "prvm_edictget"))
1188                                 goto fail;
1189
1190                 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 4), s, cmd->cvars_flagsmask, NULL);
1191         }
1192         else
1193                 Con_Printf("%s\n", s);
1194
1195 fail:
1196         ;
1197 }
1198
1199 static void PRVM_ED_GlobalGet_f(cmd_state_t *cmd)
1200 {
1201         prvm_prog_t *prog;
1202         mdef_t *key;
1203         const char *s;
1204         prvm_eval_t *v;
1205         char valuebuf[MAX_INPUTLINE];
1206
1207         if(Cmd_Argc(cmd) != 3 && Cmd_Argc(cmd) != 4)
1208         {
1209                 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1210                 return;
1211         }
1212
1213         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1214                 return;
1215
1216         key = PRVM_ED_FindGlobal(prog, Cmd_Argv(cmd, 2));
1217         if(!key)
1218         {
1219                 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(cmd, 2), Cmd_Argv(cmd, 1) );
1220                 goto fail;
1221         }
1222
1223         v = (prvm_eval_t *) &prog->globals.fp[key->ofs];
1224         s = PRVM_UglyValueString(prog, (etype_t)key->type, v, valuebuf, sizeof(valuebuf));
1225         if(Cmd_Argc(cmd) == 4)
1226         {
1227                 cvar_t *cvar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 3), cmd->cvars_flagsmask);
1228                 if (cvar)
1229                         if(Cvar_Readonly(cvar, "prvm_globalget"))
1230                                 goto fail;
1231                 Cvar_Get(cmd->cvars, Cmd_Argv(cmd, 3), s, cmd->cvars_flagsmask, NULL);
1232         }
1233         else
1234                 Con_Printf("%s\n", s);
1235
1236 fail:
1237         ;
1238 }
1239
1240 /*
1241 =============
1242 PRVM_ED_EdictSet_f
1243
1244 Console command to set a field of a specified edict
1245 =============
1246 */
1247 static void PRVM_ED_EdictSet_f(cmd_state_t *cmd)
1248 {
1249         prvm_prog_t *prog;
1250         prvm_edict_t *ed;
1251         mdef_t *key;
1252
1253         if(Cmd_Argc(cmd) != 5)
1254         {
1255                 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1256                 return;
1257         }
1258
1259         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
1260                 return;
1261
1262         ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(cmd, 2)));
1263
1264         if((key = PRVM_ED_FindField(prog, Cmd_Argv(cmd, 3))) == 0)
1265                 Con_Printf("Key %s not found!\n", Cmd_Argv(cmd, 3));
1266         else
1267                 PRVM_ED_ParseEpair(prog, ed, key, Cmd_Argv(cmd, 4), true);
1268 }
1269
1270 /*
1271 ====================
1272 PRVM_ED_ParseEdict
1273
1274 Parses an edict out of the given string, returning the new position
1275 ed should be a properly initialized empty edict.
1276 Used for initial level load and for savegames.
1277 ====================
1278 */
1279 const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_t *ent)
1280 {
1281         mdef_t *key;
1282         qbool anglehack;
1283         qbool init;
1284         char keyname[256];
1285         size_t n;
1286
1287         init = false;
1288
1289 // go through all the dictionary pairs
1290         while (1)
1291         {
1292         // parse key
1293                 if (!COM_ParseToken_Simple(&data, false, false, true))
1294                         prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1295                 if (developer_entityparsing.integer)
1296                         Con_Printf("Key: \"%s\"", com_token);
1297                 if (com_token[0] == '}')
1298                         break;
1299
1300                 // anglehack is to allow QuakeEd to write single scalar angles
1301                 // and allow them to be turned into vectors. (FIXME...)
1302                 if (!strcmp(com_token, "angle"))
1303                 {
1304                         dp_strlcpy (com_token, "angles", sizeof(com_token));
1305                         anglehack = true;
1306                 }
1307                 else
1308                         anglehack = false;
1309
1310                 // FIXME: change light to _light to get rid of this hack
1311                 if (!strcmp(com_token, "light"))
1312                         dp_strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1313
1314                 dp_strlcpy (keyname, com_token, sizeof(keyname));
1315
1316                 // another hack to fix keynames with trailing spaces
1317                 n = strlen(keyname);
1318                 while (n && keyname[n-1] == ' ')
1319                 {
1320                         keyname[n-1] = 0;
1321                         n--;
1322                 }
1323
1324         // parse value
1325                 if (!COM_ParseToken_Simple(&data, false, false, true))
1326                         prog->error_cmd("PRVM_ED_ParseEdict: EOF without closing brace");
1327                 if (developer_entityparsing.integer)
1328                         Con_Printf(" \"%s\"\n", com_token);
1329
1330                 if (com_token[0] == '}')
1331                         prog->error_cmd("PRVM_ED_ParseEdict: closing brace without data");
1332
1333                 init = true;
1334
1335                 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1336                 if (!keyname[0])
1337                         continue;
1338
1339 // keynames with a leading underscore are used for utility comments,
1340 // and are immediately discarded by quake
1341                 if (keyname[0] == '_')
1342                         continue;
1343
1344                 key = PRVM_ED_FindField (prog, keyname);
1345                 if (!key)
1346                 {
1347                         Con_DPrintf("%s: '%s' is not a field\n", prog->name, keyname);
1348                         continue;
1349                 }
1350
1351                 if (anglehack)
1352                 {
1353                         char    temp[32];
1354                         dp_strlcpy (temp, com_token, sizeof(temp));
1355                         dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1356                 }
1357
1358                 if (!PRVM_ED_ParseEpair(prog, ent, key, com_token, strcmp(keyname, "wad") != 0))
1359                         prog->error_cmd("PRVM_ED_ParseEdict: parse error");
1360         }
1361
1362         if (!init) {
1363                 ent->free = true;
1364                 ent->freetime = host.realtime;
1365         }
1366
1367         return data;
1368 }
1369
1370 void PRVM_ED_CallPrespawnFunction(prvm_prog_t *prog, prvm_edict_t *ent)
1371 {
1372         if (PRVM_serverfunction(SV_OnEntityPreSpawnFunction))
1373         {
1374                 // self = ent
1375                 PRVM_serverglobalfloat(time) = sv.time;
1376                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1377                 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPreSpawnFunction), "QC function SV_OnEntityPreSpawnFunction is missing");
1378         }
1379 }
1380
1381 qbool PRVM_ED_CallSpawnFunction(prvm_prog_t *prog, prvm_edict_t *ent, const char *data, const char *start)
1382 {
1383         const char *funcname;
1384         mfunction_t *func;
1385         prvm_eval_t *fulldata = NULL;
1386         char vabuf[1024];
1387
1388 //
1389 // immediately call spawn function, but only if there is a self global and a classname
1390 //
1391         if (!ent->free)
1392         {
1393                 if (!PRVM_alledictstring(ent, classname))
1394                 {
1395                         Con_Print("No classname for:\n");
1396                         PRVM_ED_Print(prog, ent, NULL);
1397                         PRVM_ED_Free (prog, ent);
1398                         return false;
1399                 }
1400                 /*
1401                  * This is required for FTE compatibility (FreeCS).
1402                  * It copies the key/value pairs themselves into a
1403                  * global for QC to parse on its own.
1404                  */
1405                 else if (data && start)
1406                 {
1407                         if((fulldata = PRVM_ED_FindGlobalEval(prog, "__fullspawndata")))
1408                         {
1409                                 const char *in;
1410                                 char *spawndata;
1411                                 fulldata->string = PRVM_AllocString(prog, data - start + 1, &spawndata);
1412                                 for(in = start; in < data; )
1413                                 {
1414                                         char c = *in++;
1415                                         if(c == '\n')
1416                                                 *spawndata++ = '\t';
1417                                         else
1418                                                 *spawndata++ = c;
1419                                 }
1420                                 *spawndata = 0;
1421                         }
1422                 }
1423
1424                 // look for the spawn function
1425                 funcname = PRVM_GetString(prog, PRVM_alledictstring(ent, classname));
1426                 func = PRVM_ED_FindFunction (prog, va(vabuf, sizeof(vabuf), "spawnfunc_%s", funcname));
1427                 if(!func)
1428                         if(!PRVM_allglobalfloat(require_spawnfunc_prefix))
1429                                 func = PRVM_ED_FindFunction (prog, funcname);
1430
1431                 if (!func)
1432                 {
1433                         // check for OnEntityNoSpawnFunction
1434                         if (PRVM_serverfunction(SV_OnEntityNoSpawnFunction))
1435                         {
1436                                 // self = ent
1437                                 PRVM_serverglobalfloat(time) = sv.time;
1438                                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1439                                 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityNoSpawnFunction), "QC function SV_OnEntityNoSpawnFunction is missing");
1440                         }
1441                         else
1442                         {
1443                                 
1444                                 Con_DPrint("No spawn function for:\n");
1445                                 if (developer.integer > 0) // don't confuse non-developers with errors  
1446                                         PRVM_ED_Print(prog, ent, NULL);
1447
1448                                 PRVM_ED_Free (prog, ent);
1449                                 return false; // not included in "inhibited" count
1450                         }
1451                 }
1452                 else
1453                 {
1454                         // self = ent
1455                         PRVM_serverglobalfloat(time) = sv.time;
1456                         PRVM_allglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1457                         prog->ExecuteProgram(prog, func - prog->functions, "");
1458                 }
1459                 return true;
1460         }
1461         PRVM_ED_Free(prog, ent);
1462         return false;
1463 }
1464
1465 void PRVM_ED_CallPostspawnFunction (prvm_prog_t *prog, prvm_edict_t *ent)
1466 {
1467         if(!ent->free)
1468         if (PRVM_serverfunction(SV_OnEntityPostSpawnFunction))
1469         {
1470                 // self = ent
1471                 PRVM_serverglobalfloat(time) = sv.time;
1472                 PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
1473                 prog->ExecuteProgram(prog, PRVM_serverfunction(SV_OnEntityPostSpawnFunction), "QC function SV_OnEntityPostSpawnFunction is missing");
1474         }
1475 }
1476
1477 /*
1478 ================
1479 PRVM_ED_LoadFromFile
1480
1481 The entities are directly placed in the array, rather than allocated with
1482 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1483 number references out of order.
1484
1485 Creates a server's entity / program execution context by
1486 parsing textual entity definitions out of an ent file.
1487
1488 Used for both fresh maps and savegame loads.  A fresh map would also need
1489 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1490 ================
1491 */
1492 void PRVM_ED_LoadFromFile (prvm_prog_t *prog, const char *data)
1493 {
1494         prvm_edict_t *ent;
1495         const char *start;
1496         int parsed, inhibited, spawned, died;
1497
1498         parsed = 0;
1499         inhibited = 0;
1500         spawned = 0;
1501         died = 0;
1502
1503         prvm_reuseedicts_always_allow = host.realtime;
1504
1505         // parse ents
1506         while (1)
1507         {
1508                 start = data;
1509
1510                 // parse the opening brace
1511                 if (!COM_ParseToken_Simple(&data, false, false, true))
1512                         break;
1513                 if (com_token[0] != '{')
1514                         prog->error_cmd("PRVM_ED_LoadFromFile: %s: found %s when expecting {", prog->name, com_token);
1515
1516                 // CHANGED: this is not conform to PR_LoadFromFile
1517                 if(prog->loadintoworld)
1518                 {
1519                         prog->loadintoworld = false;
1520                         ent = PRVM_EDICT_NUM(0);
1521                 }
1522                 else
1523                         ent = PRVM_ED_Alloc(prog);
1524
1525                 // clear it
1526                 if (ent != prog->edicts)        // hack
1527                         memset (ent->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t));
1528
1529                 data = PRVM_ED_ParseEdict (prog, data, ent);
1530                 parsed++;
1531
1532                 // remove the entity ?
1533                 if(!prog->load_edict(prog, ent))
1534                 {
1535                         PRVM_ED_Free(prog, ent);
1536                         inhibited++;
1537                         continue;
1538                 }
1539
1540                 PRVM_ED_CallPrespawnFunction(prog, ent);
1541
1542                 if(ent->free)
1543                 {
1544                         inhibited++;
1545                         continue;
1546                 }
1547
1548                 SV_LinkEdict(ent);
1549
1550                 if(!PRVM_ED_CallSpawnFunction(prog, ent, data, start))
1551                         continue;
1552                 
1553                 PRVM_ED_CallPostspawnFunction(prog, ent);
1554
1555                 spawned++;
1556                 if (ent->free)
1557                         died++;
1558         }
1559
1560         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);
1561
1562         prvm_reuseedicts_always_allow = 0;
1563 }
1564
1565 static void PRVM_FindOffsets(prvm_prog_t *prog)
1566 {
1567         // field and global searches use -1 for NULL
1568         memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1569         memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1570         // function searches use 0 for NULL
1571         memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1572 #define PRVM_DECLARE_serverglobalfloat(x)
1573 #define PRVM_DECLARE_serverglobalvector(x)
1574 #define PRVM_DECLARE_serverglobalstring(x)
1575 #define PRVM_DECLARE_serverglobaledict(x)
1576 #define PRVM_DECLARE_serverglobalfunction(x)
1577 #define PRVM_DECLARE_clientglobalfloat(x)
1578 #define PRVM_DECLARE_clientglobalvector(x)
1579 #define PRVM_DECLARE_clientglobalstring(x)
1580 #define PRVM_DECLARE_clientglobaledict(x)
1581 #define PRVM_DECLARE_clientglobalfunction(x)
1582 #define PRVM_DECLARE_menuglobalfloat(x)
1583 #define PRVM_DECLARE_menuglobalvector(x)
1584 #define PRVM_DECLARE_menuglobalstring(x)
1585 #define PRVM_DECLARE_menuglobaledict(x)
1586 #define PRVM_DECLARE_menuglobalfunction(x)
1587 #define PRVM_DECLARE_serverfieldfloat(x)
1588 #define PRVM_DECLARE_serverfieldvector(x)
1589 #define PRVM_DECLARE_serverfieldstring(x)
1590 #define PRVM_DECLARE_serverfieldedict(x)
1591 #define PRVM_DECLARE_serverfieldfunction(x)
1592 #define PRVM_DECLARE_clientfieldfloat(x)
1593 #define PRVM_DECLARE_clientfieldvector(x)
1594 #define PRVM_DECLARE_clientfieldstring(x)
1595 #define PRVM_DECLARE_clientfieldedict(x)
1596 #define PRVM_DECLARE_clientfieldfunction(x)
1597 #define PRVM_DECLARE_menufieldfloat(x)
1598 #define PRVM_DECLARE_menufieldvector(x)
1599 #define PRVM_DECLARE_menufieldstring(x)
1600 #define PRVM_DECLARE_menufieldedict(x)
1601 #define PRVM_DECLARE_menufieldfunction(x)
1602 #define PRVM_DECLARE_serverfunction(x)
1603 #define PRVM_DECLARE_clientfunction(x)
1604 #define PRVM_DECLARE_menufunction(x)
1605 #define PRVM_DECLARE_field(x) prog->fieldoffsets.x = PRVM_ED_FindFieldOffset(prog, #x);
1606 #define PRVM_DECLARE_global(x) prog->globaloffsets.x = PRVM_ED_FindGlobalOffset(prog, #x);
1607 #define PRVM_DECLARE_function(x) prog->funcoffsets.x = PRVM_ED_FindFunctionOffset(prog, #x);
1608 #include "prvm_offsets.h"
1609 #undef PRVM_DECLARE_serverglobalfloat
1610 #undef PRVM_DECLARE_serverglobalvector
1611 #undef PRVM_DECLARE_serverglobalstring
1612 #undef PRVM_DECLARE_serverglobaledict
1613 #undef PRVM_DECLARE_serverglobalfunction
1614 #undef PRVM_DECLARE_clientglobalfloat
1615 #undef PRVM_DECLARE_clientglobalvector
1616 #undef PRVM_DECLARE_clientglobalstring
1617 #undef PRVM_DECLARE_clientglobaledict
1618 #undef PRVM_DECLARE_clientglobalfunction
1619 #undef PRVM_DECLARE_menuglobalfloat
1620 #undef PRVM_DECLARE_menuglobalvector
1621 #undef PRVM_DECLARE_menuglobalstring
1622 #undef PRVM_DECLARE_menuglobaledict
1623 #undef PRVM_DECLARE_menuglobalfunction
1624 #undef PRVM_DECLARE_serverfieldfloat
1625 #undef PRVM_DECLARE_serverfieldvector
1626 #undef PRVM_DECLARE_serverfieldstring
1627 #undef PRVM_DECLARE_serverfieldedict
1628 #undef PRVM_DECLARE_serverfieldfunction
1629 #undef PRVM_DECLARE_clientfieldfloat
1630 #undef PRVM_DECLARE_clientfieldvector
1631 #undef PRVM_DECLARE_clientfieldstring
1632 #undef PRVM_DECLARE_clientfieldedict
1633 #undef PRVM_DECLARE_clientfieldfunction
1634 #undef PRVM_DECLARE_menufieldfloat
1635 #undef PRVM_DECLARE_menufieldvector
1636 #undef PRVM_DECLARE_menufieldstring
1637 #undef PRVM_DECLARE_menufieldedict
1638 #undef PRVM_DECLARE_menufieldfunction
1639 #undef PRVM_DECLARE_serverfunction
1640 #undef PRVM_DECLARE_clientfunction
1641 #undef PRVM_DECLARE_menufunction
1642 #undef PRVM_DECLARE_field
1643 #undef PRVM_DECLARE_global
1644 #undef PRVM_DECLARE_function
1645 }
1646
1647 // not used
1648 /*
1649 typedef struct dpfield_s
1650 {
1651         int type;
1652         char *string;
1653 }
1654 dpfield_t;
1655
1656 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1657
1658 dpfield_t dpfields[] =
1659 {
1660 };
1661 */
1662
1663 /*
1664 ===============
1665 PRVM_ResetProg
1666 ===============
1667 */
1668
1669 #define PO_HASHSIZE 16384
1670 typedef struct po_string_s
1671 {
1672         char *key, *value;
1673         struct po_string_s *nextonhashchain;
1674 }
1675 po_string_t;
1676 typedef struct po_s
1677 {
1678         po_string_t *hashtable[PO_HASHSIZE];
1679 }
1680 po_t;
1681 static void PRVM_PO_UnparseString(char *out, const char *in, size_t outsize)
1682 {
1683         for(;;)
1684         {
1685                 switch(*in)
1686                 {
1687                         case 0:
1688                                 *out++ = 0;
1689                                 return;
1690                         case '\a': if(outsize >= 2) { *out++ = '\\'; *out++ = 'a'; outsize -= 2; } break;
1691                         case '\b': if(outsize >= 2) { *out++ = '\\'; *out++ = 'b'; outsize -= 2; } break;
1692                         case '\t': if(outsize >= 2) { *out++ = '\\'; *out++ = 't'; outsize -= 2; } break;
1693                         case '\r': if(outsize >= 2) { *out++ = '\\'; *out++ = 'r'; outsize -= 2; } break;
1694                         case '\n': if(outsize >= 2) { *out++ = '\\'; *out++ = 'n'; outsize -= 2; } break;
1695                         case '\\': if(outsize >= 2) { *out++ = '\\'; *out++ = '\\'; outsize -= 2; } break;
1696                         case '"': if(outsize >= 2) { *out++ = '\\'; *out++ = '"'; outsize -= 2; } break;
1697                         default:
1698                                 if(*in >= 0 && *in <= 0x1F)
1699                                 {
1700                                         if(outsize >= 4)
1701                                         {
1702                                                 *out++ = '\\';
1703                                                 *out++ = '0' + ((*in & 0700) >> 6);
1704                                                 *out++ = '0' + ((*in & 0070) >> 3);
1705                                                 *out++ = '0' +  (*in & 0007)      ;
1706                                                 outsize -= 4;
1707                                         }
1708                                 }
1709                                 else
1710                                 {
1711                                         if(outsize >= 1)
1712                                         {
1713                                                 *out++ = *in;
1714                                                 outsize -= 1;
1715                                         }
1716                                 }
1717                                 break;
1718                 }
1719                 ++in;
1720         }
1721 }
1722 static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize)
1723 {
1724         for(;;)
1725         {
1726                 switch(*in)
1727                 {
1728                         case 0:
1729                                 *out++ = 0;
1730                                 return;
1731                         case '\\':
1732                                 ++in;
1733                                 switch(*in)
1734                                 {
1735                                         case 'a': if(outsize > 0) { *out++ = '\a'; --outsize; } break;
1736                                         case 'b': if(outsize > 0) { *out++ = '\b'; --outsize; } break;
1737                                         case 't': if(outsize > 0) { *out++ = '\t'; --outsize; } break;
1738                                         case 'r': if(outsize > 0) { *out++ = '\r'; --outsize; } break;
1739                                         case 'n': if(outsize > 0) { *out++ = '\n'; --outsize; } break;
1740                                         case '\\': if(outsize > 0) { *out++ = '\\'; --outsize; } break;
1741                                         case '"': if(outsize > 0) { *out++ = '"'; --outsize; } break;
1742                                         case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
1743                                                 if(outsize > 0)
1744                                                         *out = *in - '0';
1745                                                 ++in;
1746                                                 if(*in >= '0' && *in <= '7')
1747                                                 {
1748                                                         if(outsize > 0)
1749                                                                 *out = (*out << 3) | (*in - '0');
1750                                                         ++in;
1751                                                 }
1752                                                 if(*in >= '0' && *in <= '7')
1753                                                 {
1754                                                         if(outsize > 0)
1755                                                                 *out = (*out << 3) | (*in - '0');
1756                                                         ++in;
1757                                                 }
1758                                                 --in;
1759                                                 if(outsize > 0)
1760                                                 {
1761                                                         ++out;
1762                                                         --outsize;
1763                                                 }
1764                                                 break;
1765                                         default:
1766                                                 if(outsize > 0) { *out++ = *in; --outsize; }
1767                                                 break;
1768                                 }
1769                                 break;
1770                         default:
1771                                 if(outsize > 0)
1772                                 {
1773                                         *out++ = *in;
1774                                         --outsize;
1775                                 }
1776                                 break;
1777                 }
1778                 ++in;
1779         }
1780 }
1781 static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool)
1782 {
1783         po_t *po = NULL;
1784         const char *p, *q;
1785         int mode;
1786         char inbuf[MAX_INPUTLINE];
1787         char decodedbuf[MAX_INPUTLINE];
1788         size_t decodedpos;
1789         int hashindex;
1790         po_string_t thisstr;
1791         int i;
1792
1793         for (i = 0; i < 2; ++i)
1794         {
1795                 const char *buf = (const char *)
1796                         FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
1797                 // first read filename2, then read filename
1798                 // so that progs.dat.de.po wins over common.de.po
1799                 // and within file, last item wins
1800
1801                 if(!buf)
1802                         continue;
1803
1804                 if (!po)
1805                 {
1806                         po = (po_t *)Mem_Alloc(pool, sizeof(*po));
1807                         memset(po, 0, sizeof(*po));
1808                 }
1809
1810                 memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
1811
1812                 p = buf;
1813                 while(*p)
1814                 {
1815                         if(*p == '#')
1816                         {
1817                                 // skip to newline
1818                                 p = strchr(p, '\n');
1819                                 if(!p)
1820                                         break;
1821                                 ++p;
1822                                 continue;
1823                         }
1824                         if(*p == '\r' || *p == '\n')
1825                         {
1826                                 ++p;
1827                                 continue;
1828                         }
1829                         if(!strncmp(p, "msgid \"", 7))
1830                         {
1831                                 mode = 0;
1832                                 p += 6;
1833                         }
1834                         else if(!strncmp(p, "msgstr \"", 8))
1835                         {
1836                                 mode = 1;
1837                                 p += 7;
1838                         }
1839                         else
1840                         {
1841                                 p = strchr(p, '\n');
1842                                 if(!p)
1843                                         break;
1844                                 ++p;
1845                                 continue;
1846                         }
1847                         decodedpos = 0;
1848                         while(*p == '"')
1849                         {
1850                                 ++p;
1851                                 q = strchr(p, '\n');
1852                                 if(!q)
1853                                         break;
1854                                 if(*(q-1) == '\r')
1855                                         --q;
1856                                 if(*(q-1) != '"')
1857                                         break;
1858                                 if((size_t)(q - p) >= (size_t) sizeof(inbuf))
1859                                         break;
1860                                 memcpy(inbuf, p, q - p - 1);
1861                                 inbuf[q - p - 1] = '\0';
1862                                 PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
1863                                 decodedpos += strlen(decodedbuf + decodedpos);
1864                                 if(*q == '\r')
1865                                         ++q;
1866                                 if(*q == '\n')
1867                                         ++q;
1868                                 p = q;
1869                         }
1870                         if(mode == 0)
1871                         {
1872                                 if(thisstr.key)
1873                                         Mem_Free(thisstr.key);
1874                                 thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
1875                                 memcpy(thisstr.key, decodedbuf, decodedpos + 1);
1876                         }
1877                         else if(decodedpos > 0 && thisstr.key) // skip empty translation results
1878                         {
1879                                 thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
1880                                 memcpy(thisstr.value, decodedbuf, decodedpos + 1);
1881                                 hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
1882                                 thisstr.nextonhashchain = po->hashtable[hashindex];
1883                                 po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
1884                                 memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
1885                                 memset(&thisstr, 0, sizeof(thisstr));
1886                         }
1887                 }
1888
1889                 Mem_Free((char *) buf);
1890         }
1891
1892         return po;
1893 }
1894 static const char *PRVM_PO_Lookup(po_t *po, const char *str)
1895 {
1896         int hashindex = CRC_Block((const unsigned char *) str, strlen(str)) % PO_HASHSIZE;
1897         po_string_t *p = po->hashtable[hashindex];
1898         while(p)
1899         {
1900                 if(!strcmp(str, p->key))
1901                         return p->value;
1902                 p = p->nextonhashchain;
1903         }
1904         return NULL;
1905 }
1906 static void PRVM_PO_Destroy(po_t *po)
1907 {
1908         int i;
1909         for(i = 0; i < PO_HASHSIZE; ++i)
1910         {
1911                 po_string_t *p = po->hashtable[i];
1912                 while(p)
1913                 {
1914                         po_string_t *q = p;
1915                         p = p->nextonhashchain;
1916                         Mem_Free(q->key);
1917                         Mem_Free(q->value);
1918                         Mem_Free(q);
1919                 }
1920         }
1921         Mem_Free(po);
1922 }
1923
1924 void PRVM_LeakTest(prvm_prog_t *prog);
1925 void PRVM_Prog_Reset(prvm_prog_t *prog)
1926 {
1927         if (prog->loaded)
1928         {
1929                 if(prog->tempstringsbuf.cursize)
1930                         Mem_Free(prog->tempstringsbuf.data);
1931                 prog->tempstringsbuf.cursize = 0;
1932                 PRVM_LeakTest(prog);
1933                 prog->reset_cmd(prog);
1934                 Mem_FreePool(&prog->progs_mempool);
1935                 if(prog->po)
1936                         PRVM_PO_Destroy((po_t *) prog->po);
1937         }
1938         memset(prog,0,sizeof(prvm_prog_t));
1939         prog->break_statement = -1;
1940         prog->watch_global_type = ev_void;
1941         prog->watch_field_type = ev_void;
1942 }
1943
1944 /*
1945 ===============
1946 PRVM_LoadLNO
1947 ===============
1948 */
1949 static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
1950         fs_offset_t filesize;
1951         unsigned char *lno;
1952         unsigned int *header;
1953         char filename[512];
1954
1955         FS_StripExtension( progname, filename, sizeof( filename ) );
1956         dp_strlcat( filename, ".lno", sizeof( filename ) );
1957
1958         lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1959         if( !lno ) {
1960                 return;
1961         }
1962
1963 /*
1964 <Spike>    SafeWrite (h, &lnotype, sizeof(int));
1965 <Spike>    SafeWrite (h, &version, sizeof(int));
1966 <Spike>    SafeWrite (h, &numglobaldefs, sizeof(int));
1967 <Spike>    SafeWrite (h, &numpr_globals, sizeof(int));
1968 <Spike>    SafeWrite (h, &numfielddefs, sizeof(int));
1969 <Spike>    SafeWrite (h, &numstatements, sizeof(int));
1970 <Spike>    SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1971 */
1972         if ((unsigned int)filesize < (6 + prog->progs_numstatements) * sizeof(int))
1973         {
1974                 Mem_Free(lno);
1975                 return;
1976         }
1977
1978         header = (unsigned int *) lno;
1979         if (memcmp(lno, "LNOF", 4) == 0
1980         && LittleLong( header[ 1 ] ) == 1
1981         && (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs_numglobaldefs
1982         && (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs_numglobals
1983         && (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs_numfielddefs
1984         && (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements)
1985         {
1986                 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1987                 memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
1988
1989                 /* gmqcc suports columnums */
1990                 if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
1991                 {
1992                         prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
1993                         memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
1994                 }
1995         }
1996         Mem_Free( lno );
1997 }
1998
1999 /*
2000 ===============
2001 PRVM_Prog_Load
2002 ===============
2003 */
2004 static void PRVM_UpdateBreakpoints(prvm_prog_t *prog);
2005 void PRVM_Prog_Load(prvm_prog_t *prog, const char *filename, unsigned char *data, fs_offset_t size, void CheckRequiredFuncs(prvm_prog_t *prog, const char *filename), int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global)
2006 {
2007         int i;
2008         dprograms_t *dprograms;
2009
2010         dstatement16_t *instatements16;
2011         dstatement32_t *instatements32;
2012         ddef16_t *infielddefs16;
2013         ddef32_t *infielddefs32;
2014         ddef16_t *inglobaldefs16;
2015         ddef32_t *inglobaldefs32;
2016
2017         int *inglobals;
2018         dfunction_t *infunctions;
2019         char *instrings;
2020         fs_offset_t filesize;
2021         int requiredglobalspace;
2022         opcode_t op;
2023         int a;
2024         int b;
2025         int c;
2026         union
2027         {
2028                 unsigned int i;
2029                 float f;
2030         }
2031         u;
2032         unsigned int d;
2033         char vabuf[1024];
2034         char vabuf2[1024];
2035         cvar_t *cvar;
2036         int structtype = 0;
2037
2038         if (prog->loaded)
2039                 prog->error_cmd("%s: there is already a %s program loaded!", __func__, prog->name);
2040
2041         Host_LockSession(); // all progs can use the session cvar
2042         Crypto_LoadKeys(); // all progs might use the keys at init time
2043
2044         if (data)
2045         {
2046                 dprograms = (dprograms_t *) data;
2047                 filesize = size;
2048         }
2049         else
2050                 dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
2051         if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
2052                 prog->error_cmd("%s: couldn't load \"%s\" for %s", __func__, filename, prog->name);
2053         // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
2054
2055         prog->profiletime = Sys_DirtyTime();
2056         prog->starttime = host.realtime;
2057
2058         requiredglobalspace = 0;
2059         for (i = 0;i < numrequiredglobals;i++)
2060                 requiredglobalspace += required_global[i].type == ev_vector ? 3 : 1;
2061
2062         prog->filecrc = CRC_Block((unsigned char *)dprograms, filesize);
2063
2064 // byte swap the header
2065         prog->progs_version = LittleLong(dprograms->version);
2066         prog->progs_crc = LittleLong(dprograms->crc);
2067         if (prog->progs_version == 7)
2068         {
2069                 dprograms_v7_t *v7 = (dprograms_v7_t*)dprograms;
2070                 structtype = LittleLong(v7->secondaryversion);
2071                 if (structtype == PROG_SECONDARYVERSION16 ||
2072                         structtype == PROG_SECONDARYVERSION32) // barely supported
2073                         Con_Printf(CON_WARN "WARNING: %s: %s targets FTEQW, for which support is incomplete. Proceed at your own risk.\n", prog->name, filename);
2074                 else
2075                         prog->error_cmd("%s: %s targets unknown engine", prog->name, filename);
2076
2077                 if (v7->numbodylessfuncs != 0 || v7->numtypes != 0 || v7->blockscompressed != 0)
2078                         prog->error_cmd("%s: %s uses unsupported features.", prog->name, filename);
2079         }
2080         else if (prog->progs_version != PROG_VERSION)
2081                 prog->error_cmd("%s: %s has wrong version number (%i should be %i)", prog->name, filename, prog->progs_version, PROG_VERSION);
2082         instatements16 = (dstatement16_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_statements));
2083         instatements32 = (dstatement32_t *)instatements16;
2084         prog->progs_numstatements = LittleLong(dprograms->numstatements);
2085         inglobaldefs16 = (ddef16_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globaldefs));
2086         inglobaldefs32 = (ddef32_t *)inglobaldefs16;
2087         prog->progs_numglobaldefs = LittleLong(dprograms->numglobaldefs);
2088         infielddefs16 = (ddef16_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_fielddefs));
2089         infielddefs32 = (ddef32_t *)infielddefs16;
2090         prog->progs_numfielddefs = LittleLong(dprograms->numfielddefs);
2091         infunctions = (dfunction_t *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_functions));
2092         prog->progs_numfunctions = LittleLong(dprograms->numfunctions);
2093         instrings = (char *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_strings));
2094         prog->progs_numstrings = LittleLong(dprograms->numstrings);
2095         inglobals = (int *)((unsigned char *)dprograms + LittleLong(dprograms->ofs_globals));
2096         prog->progs_numglobals = LittleLong(dprograms->numglobals);
2097         prog->progs_entityfields = LittleLong(dprograms->entityfields);
2098
2099         prog->numstatements = prog->progs_numstatements;
2100         prog->numglobaldefs = prog->progs_numglobaldefs;
2101         prog->numfielddefs = prog->progs_numfielddefs;
2102         prog->numfunctions = prog->progs_numfunctions;
2103         prog->numstrings = prog->progs_numstrings;
2104         prog->numglobals = prog->progs_numglobals;
2105         prog->entityfields = prog->progs_entityfields;
2106
2107         if (LittleLong(dprograms->ofs_strings) + prog->progs_numstrings > (int)filesize)
2108                 prog->error_cmd("%s: %s strings go past end of file", prog->name, filename);
2109         prog->strings = (char *)Mem_Alloc(prog->progs_mempool, prog->progs_numstrings);
2110         memcpy(prog->strings, instrings, prog->progs_numstrings);
2111         prog->stringssize = prog->progs_numstrings;
2112
2113         prog->numknownstrings = 0;
2114         prog->maxknownstrings = 0;
2115         prog->knownstrings = NULL;
2116         prog->knownstrings_flags = NULL;
2117
2118         Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
2119
2120         // we need to expand the globaldefs and fielddefs to include engine defs
2121         prog->globaldefs = (mdef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(mdef_t));
2122         prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t));
2123                 // + 2 is because of an otherwise occurring overrun in RETURN instruction
2124                 // when trying to return the last or second-last global
2125                 // (RETURN always returns a vector, there is no RETURN_F instruction)
2126         prog->fielddefs = (mdef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(mdef_t));
2127         // we need to convert the statements to our memory format
2128         prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
2129         // allocate space for profiling statement usage
2130         prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2131         prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
2132         // functions need to be converted to the memory format
2133         prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
2134
2135         for (i = 0;i < prog->progs_numfunctions;i++)
2136         {
2137                 prog->functions[i].first_statement = LittleLong(infunctions[i].first_statement);
2138                 prog->functions[i].parm_start = LittleLong(infunctions[i].parm_start);
2139                 prog->functions[i].s_name = LittleLong(infunctions[i].s_name);
2140                 prog->functions[i].s_file = LittleLong(infunctions[i].s_file);
2141                 prog->functions[i].numparms = LittleLong(infunctions[i].numparms);
2142                 prog->functions[i].locals = LittleLong(infunctions[i].locals);
2143                 memcpy(prog->functions[i].parm_size, infunctions[i].parm_size, sizeof(infunctions[i].parm_size));
2144                 if(prog->functions[i].first_statement >= prog->numstatements)
2145                         prog->error_cmd("%s: out of bounds function statement (function %d) in %s", __func__, i, prog->name);
2146                 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
2147         }
2148
2149         // copy the globaldefs to the new globaldefs list
2150         switch(structtype)
2151         {
2152         case PROG_SECONDARYVERSION32:
2153                 for (i=0 ; i<prog->numglobaldefs ; i++)
2154                 {
2155                         prog->globaldefs[i].type = LittleLong(inglobaldefs32[i].type);
2156                         prog->globaldefs[i].ofs = LittleLong(inglobaldefs32[i].ofs);
2157                         prog->globaldefs[i].s_name = LittleLong(inglobaldefs32[i].s_name);
2158                         // TODO bounds check ofs, s_name
2159                 }
2160                 break;
2161         default:
2162                 for (i=0 ; i<prog->numglobaldefs ; i++)
2163                 {
2164                         prog->globaldefs[i].type = (unsigned short)LittleShort(inglobaldefs16[i].type);
2165                         prog->globaldefs[i].ofs = (unsigned short)LittleShort(inglobaldefs16[i].ofs);
2166                         prog->globaldefs[i].s_name = LittleLong(inglobaldefs16[i].s_name);
2167                         // TODO bounds check ofs, s_name
2168                 }
2169                 break;
2170         }
2171
2172         // append the required globals
2173         for (i = 0;i < numrequiredglobals;i++)
2174         {
2175                 prog->globaldefs[prog->numglobaldefs].type = required_global[i].type;
2176                 prog->globaldefs[prog->numglobaldefs].ofs = prog->numglobals;
2177                 prog->globaldefs[prog->numglobaldefs].s_name = PRVM_SetEngineString(prog, required_global[i].name);
2178                 if (prog->globaldefs[prog->numglobaldefs].type == ev_vector)
2179                         prog->numglobals += 3;
2180                 else
2181                         prog->numglobals++;
2182                 prog->numglobaldefs++;
2183         }
2184
2185         // copy the progs fields to the new fields list
2186         switch(structtype)
2187         {
2188         case PROG_SECONDARYVERSION32:
2189                 for (i = 0;i < prog->numfielddefs;i++)
2190                 {
2191                         prog->fielddefs[i].type = LittleLong(infielddefs32[i].type);
2192                         if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2193                                 prog->error_cmd("%s: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", __func__, prog->name);
2194                         prog->fielddefs[i].ofs = LittleLong(infielddefs32[i].ofs);
2195                         prog->fielddefs[i].s_name = LittleLong(infielddefs32[i].s_name);
2196                         // TODO bounds check ofs, s_name
2197                 }
2198                 break;
2199         default:
2200                 for (i = 0;i < prog->numfielddefs;i++)
2201                 {
2202                         prog->fielddefs[i].type = (unsigned short)LittleShort(infielddefs16[i].type);
2203                         if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
2204                                 prog->error_cmd("%s: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", __func__, prog->name);
2205                         prog->fielddefs[i].ofs = (unsigned short)LittleShort(infielddefs16[i].ofs);
2206                         prog->fielddefs[i].s_name = LittleLong(infielddefs16[i].s_name);
2207                         // TODO bounds check ofs, s_name
2208                 }
2209                 break;
2210         }
2211
2212         // append the required fields
2213         for (i = 0;i < numrequiredfields;i++)
2214         {
2215                 prog->fielddefs[prog->numfielddefs].type = required_field[i].type;
2216                 prog->fielddefs[prog->numfielddefs].ofs = prog->entityfields;
2217                 prog->fielddefs[prog->numfielddefs].s_name = PRVM_SetEngineString(prog, required_field[i].name);
2218                 if (prog->fielddefs[prog->numfielddefs].type == ev_vector)
2219                         prog->entityfields += 3;
2220                 else
2221                         prog->entityfields++;
2222                 prog->numfielddefs++;
2223         }
2224
2225         // LadyHavoc: TODO: reorder globals to match engine struct
2226         // LadyHavoc: TODO: reorder fields to match engine struct
2227 #define remapglobal(index) (index)
2228 #define remapfield(index) (index)
2229
2230         // copy globals
2231         // FIXME: LadyHavoc: this uses a crude way to identify integer constants, rather than checking for matching globaldefs and checking their type
2232         for (i = 0;i < prog->progs_numglobals;i++)
2233         {
2234                 u.i = LittleLong(inglobals[i]);
2235                 // most globals are 0, we only need to deal with the ones that are not
2236                 if (u.i)
2237                 {
2238                         d = u.i & 0xFF800000;
2239                         if ((d == 0xFF800000) || (d == 0))
2240                         {
2241                                 // Looks like an integer (expand to int64)
2242                                 prog->globals.ip[remapglobal(i)] = u.i;
2243                         }
2244                         else
2245                         {
2246                                 // Looks like a float (expand to double)
2247                                 prog->globals.fp[remapglobal(i)] = u.f;
2248                         }
2249                 }
2250         }
2251
2252         // copy, remap globals in statements, bounds check
2253         for (i = 0;i < prog->progs_numstatements;i++)
2254         {
2255                 switch(structtype)
2256                 {
2257                 case PROG_SECONDARYVERSION32:
2258                         op = (opcode_t)LittleLong(instatements32[i].op);
2259                         a = (unsigned int)LittleLong(instatements32[i].a);
2260                         b = (unsigned int)LittleLong(instatements32[i].b);
2261                         c = (unsigned int)LittleLong(instatements32[i].c);
2262                         break;
2263                 default:
2264                         op = (opcode_t)LittleShort(instatements16[i].op);
2265                         a = (unsigned short)LittleShort(instatements16[i].a);
2266                         b = (unsigned short)LittleShort(instatements16[i].b);
2267                         c = (unsigned short)LittleShort(instatements16[i].c);
2268                         break;
2269                 }
2270                 switch (op)
2271                 {
2272                 case OP_IF:
2273                 case OP_IFNOT:
2274                         b = (short)b;
2275                         if (a >= prog->progs_numglobals || b + i < 0 || b + i >= prog->progs_numstatements)
2276                                 prog->error_cmd("%s: out of bounds IF/IFNOT (statement %d) in %s", __func__, i, prog->name);
2277                         prog->statements[i].op = op;
2278                         prog->statements[i].operand[0] = remapglobal(a);
2279                         prog->statements[i].operand[1] = -1;
2280                         prog->statements[i].operand[2] = -1;
2281                         prog->statements[i].jumpabsolute = i + b;
2282                         break;
2283                 case OP_GOTO:
2284                         a = (short)a;
2285                         if (a + i < 0 || a + i >= prog->progs_numstatements)
2286                                 prog->error_cmd("%s: out of bounds GOTO (statement %d) in %s", __func__, i, prog->name);
2287                         prog->statements[i].op = op;
2288                         prog->statements[i].operand[0] = -1;
2289                         prog->statements[i].operand[1] = -1;
2290                         prog->statements[i].operand[2] = -1;
2291                         prog->statements[i].jumpabsolute = i + a;
2292                         break;
2293                 default:
2294                         Con_DPrintf("%s: unknown opcode %d at statement %d in %s\n", __func__, (int)op, i, prog->name);
2295
2296                         //make sure its something well defined.
2297                         prog->statements[i].op = OP_BOUNDCHECK;
2298                         prog->statements[i].operand[0] = 0;
2299                         prog->statements[i].operand[1] =
2300                         prog->statements[i].operand[2] = op;
2301                         prog->statements[i].jumpabsolute = -1;
2302                         break;
2303                 case OP_STORE_I:
2304                 case OP_ADD_I:
2305                 case OP_ADD_FI:
2306                 case OP_ADD_IF:
2307                 case OP_SUB_I:
2308                 case OP_SUB_FI:
2309                 case OP_SUB_IF:
2310                 case OP_CONV_IF:
2311                 case OP_CONV_FI:
2312                 case OP_LOAD_I:
2313                 case OP_STOREP_I:
2314                 case OP_BITAND_I:
2315                 case OP_BITOR_I:
2316                 case OP_MUL_I:
2317                 case OP_DIV_I:
2318                 case OP_EQ_I:
2319                 case OP_NE_I:
2320                 case OP_NOT_I:
2321                 case OP_DIV_VF:
2322                 case OP_STORE_P:
2323                 case OP_LE_I:
2324                 case OP_GE_I:
2325                 case OP_LT_I:
2326                 case OP_GT_I:
2327                 case OP_LE_IF:
2328                 case OP_GE_IF:
2329                 case OP_LT_IF:
2330                 case OP_GT_IF:
2331                 case OP_LE_FI:
2332                 case OP_GE_FI:
2333                 case OP_LT_FI:
2334                 case OP_GT_FI:
2335                 case OP_EQ_IF:
2336                 case OP_EQ_FI:
2337                 case OP_MUL_IF:
2338                 case OP_MUL_FI:
2339                 case OP_MUL_VI:
2340                 case OP_DIV_IF:
2341                 case OP_DIV_FI:
2342                 case OP_BITAND_IF:
2343                 case OP_BITOR_IF:
2344                 case OP_BITAND_FI:
2345                 case OP_BITOR_FI:
2346                 case OP_AND_I:
2347                 case OP_OR_I:
2348                 case OP_AND_IF:
2349                 case OP_OR_IF:
2350                 case OP_AND_FI:
2351                 case OP_OR_FI:
2352                 case OP_NE_IF:
2353                 case OP_NE_FI:
2354                 case OP_GSTOREP_I:
2355                 case OP_GSTOREP_F:
2356                 case OP_GSTOREP_ENT:
2357                 case OP_GSTOREP_FLD:
2358                 case OP_GSTOREP_S:
2359                 case OP_GSTOREP_FNC:
2360                 case OP_GSTOREP_V:
2361 //              case OP_GADDRESS:
2362                 case OP_GLOAD_I:
2363                 case OP_GLOAD_F:
2364                 case OP_GLOAD_FLD:
2365                 case OP_GLOAD_ENT:
2366                 case OP_GLOAD_S:
2367                 case OP_GLOAD_FNC:
2368                 case OP_BOUNDCHECK:
2369                 case OP_GLOAD_V:
2370
2371                 // global global global
2372                 case OP_ADD_F:
2373                 case OP_ADD_V:
2374                 case OP_SUB_F:
2375                 case OP_SUB_V:
2376                 case OP_MUL_F:
2377                 case OP_MUL_V:
2378                 case OP_MUL_FV:
2379                 case OP_MUL_VF:
2380                 case OP_DIV_F:
2381                 case OP_BITAND:
2382                 case OP_BITOR:
2383                 case OP_GE:
2384                 case OP_LE:
2385                 case OP_GT:
2386                 case OP_LT:
2387                 case OP_AND:
2388                 case OP_OR:
2389                 case OP_EQ_F:
2390                 case OP_EQ_V:
2391                 case OP_EQ_S:
2392                 case OP_EQ_E:
2393                 case OP_EQ_FNC:
2394                 case OP_NE_F:
2395                 case OP_NE_V:
2396                 case OP_NE_S:
2397                 case OP_NE_E:
2398                 case OP_NE_FNC:
2399                 case OP_ADDRESS:
2400                 case OP_LOAD_F:
2401                 case OP_LOAD_FLD:
2402                 case OP_LOAD_ENT:
2403                 case OP_LOAD_S:
2404                 case OP_LOAD_FNC:
2405                 case OP_LOAD_V:
2406                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals || c >= prog->progs_numglobals)
2407                                 prog->error_cmd("%s: out of bounds global index (statement %d)", __func__, i);
2408                         prog->statements[i].op = op;
2409                         prog->statements[i].operand[0] = remapglobal(a);
2410                         prog->statements[i].operand[1] = remapglobal(b);
2411                         prog->statements[i].operand[2] = remapglobal(c);
2412                         prog->statements[i].jumpabsolute = -1;
2413                         break;
2414                 // global none global
2415                 case OP_NOT_F:
2416                 case OP_NOT_V:
2417                 case OP_NOT_S:
2418                 case OP_NOT_FNC:
2419                 case OP_NOT_ENT:
2420                         if (a >= prog->progs_numglobals || c >= prog->progs_numglobals)
2421                                 prog->error_cmd("%s: out of bounds global index (statement %d) in %s", __func__, i, prog->name);
2422                         prog->statements[i].op = op;
2423                         prog->statements[i].operand[0] = remapglobal(a);
2424                         prog->statements[i].operand[1] = -1;
2425                         prog->statements[i].operand[2] = remapglobal(c);
2426                         prog->statements[i].jumpabsolute = -1;
2427                         break;
2428                 // 2 globals
2429                 case OP_STOREP_F:
2430                 case OP_STOREP_ENT:
2431                 case OP_STOREP_FLD:
2432                 case OP_STOREP_S:
2433                 case OP_STOREP_FNC:
2434                         if (c)  //Spike -- DP is alergic to pointers in QC. Try to avoid too many nasty surprises.
2435                                 Con_DPrintf("%s: storep-with-offset is not permitted in %s\n", __func__, prog->name);
2436                         //fallthrough
2437                 case OP_STORE_F:
2438                 case OP_STORE_ENT:
2439                 case OP_STORE_FLD:
2440                 case OP_STORE_S:
2441                 case OP_STORE_FNC:
2442                 case OP_STATE:
2443                 case OP_STOREP_V:
2444                 case OP_STORE_V:
2445                         if (a >= prog->progs_numglobals || b >= prog->progs_numglobals)
2446                                 prog->error_cmd("%s: out of bounds global index (statement %d) in %s", __func__, i, prog->name);
2447                         prog->statements[i].op = op;
2448                         prog->statements[i].operand[0] = remapglobal(a);
2449                         prog->statements[i].operand[1] = remapglobal(b);
2450                         prog->statements[i].operand[2] = -1;
2451                         prog->statements[i].jumpabsolute = -1;
2452                         break;
2453                 // 1 global
2454                 case OP_CALL0:
2455                         if ( a < prog->progs_numglobals)
2456                                 if ( prog->globals.ip[remapglobal(a)] >= 0 )
2457                                         if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
2458                                                 if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
2459                                                         ++prog->numexplicitcoveragestatements;
2460                 case OP_CALL1:
2461                 case OP_CALL2:
2462                 case OP_CALL3:
2463                 case OP_CALL4:
2464                 case OP_CALL5:
2465                 case OP_CALL6:
2466                 case OP_CALL7:
2467                 case OP_CALL8:
2468                 case OP_DONE:
2469                 case OP_RETURN:
2470                         if ( a >= prog->progs_numglobals)
2471                                 prog->error_cmd("%s: out of bounds global index (statement %d) in %s", __func__, i, prog->name);
2472                         if (b || c)     //Spike -- added this check just as a diagnostic...
2473                                 Con_DPrintf("%s: unexpected offset on call opcode in %s. Hexen2 format is not supported\n", __func__, prog->name);
2474                         prog->statements[i].op = op;
2475                         prog->statements[i].operand[0] = remapglobal(a);
2476                         prog->statements[i].operand[1] = -1;
2477                         prog->statements[i].operand[2] = -1;
2478                         prog->statements[i].jumpabsolute = -1;
2479                         break;
2480                 }
2481         }
2482         if(prog->numstatements < 1)
2483         {
2484                 prog->error_cmd("%s: empty program in %s", __func__, prog->name);
2485         }
2486         else switch(prog->statements[prog->numstatements - 1].op)
2487         {
2488                 case OP_RETURN:
2489                 case OP_GOTO:
2490                 case OP_DONE:
2491                         break;
2492                 default:
2493                         prog->error_cmd("%s: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", __func__, prog->name);
2494                         break;
2495         }
2496
2497         // we're done with the file now
2498         if(!data)
2499                 Mem_Free(dprograms);
2500         dprograms = NULL;
2501
2502         prog->flag = 0;
2503         // expected to not return (call prog->error_cmd) if checks fail
2504         CheckRequiredFuncs(prog, filename);
2505
2506         PRVM_LoadLNO(prog, filename);
2507
2508         PRVM_Init_Exec(prog);
2509
2510         if(*prvm_language.string)
2511         // in CSQC we really shouldn't be able to change how stuff works... sorry for now
2512         // later idea: include a list of authorized .po file checksums with the csprogs
2513         {
2514                 qbool deftrans = prog == CLVM_prog;
2515                 const char *realfilename = (prog != CLVM_prog ? filename : csqc_progname.string);
2516                 if(deftrans) // once we have dotranslate_ strings, ALWAYS use the opt-in method!
2517                 {
2518                         for (i=0 ; i<prog->numglobaldefs ; i++)
2519                         {
2520                                 const char *name;
2521                                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2522                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2523                                 if(name && !strncmp(name, "dotranslate_", 12))
2524                                 {
2525                                         deftrans = false;
2526                                         break;
2527                                 }
2528                         }
2529                 }
2530                 if(!strcmp(prvm_language.string, "dump"))
2531                 {
2532                         qfile_t *f = FS_OpenRealFile(va(vabuf, sizeof(vabuf), "%s.pot", realfilename), "w", false);
2533                         Con_Printf("Dumping to %s.pot\n", realfilename);
2534                         if(f)
2535                         {
2536                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2537                                 {
2538                                         const char *name;
2539                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2540                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2541                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2542                                         {
2543                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2544                                                 const char *value = PRVM_GetString(prog, val->string);
2545                                                 if(*value)
2546                                                 {
2547                                                         char buf[MAX_INPUTLINE];
2548                                                         PRVM_PO_UnparseString(buf, value, sizeof(buf));
2549                                                         FS_Printf(f, "msgid \"%s\"\nmsgstr \"\"\n\n", buf);
2550                                                 }
2551                                         }
2552                                 }
2553                                 FS_Close(f);
2554                         }
2555                 }
2556                 else
2557                 {
2558                         po_t *po = PRVM_PO_Load(
2559                                         va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string),
2560                                         va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string),
2561                                         prog->progs_mempool);
2562                         if(po)
2563                         {
2564                                 for (i=0 ; i<prog->numglobaldefs ; i++)
2565                                 {
2566                                         const char *name;
2567                                         name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2568                                         if(deftrans ? (!name || strncmp(name, "notranslate_", 12)) : (name && !strncmp(name, "dotranslate_", 12)))
2569                                         if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2570                                         {
2571                                                 prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2572                                                 const char *value = PRVM_GetString(prog, val->string);
2573                                                 if(*value)
2574                                                 {
2575                                                         value = PRVM_PO_Lookup(po, value);
2576                                                         if(value)
2577                                                                 val->string = PRVM_SetEngineString(prog, value);
2578                                                 }
2579                                         }
2580                                 }
2581                         }
2582                 }
2583         }
2584
2585         for (cvar = prog->console_cmd->cvars->vars; cvar; cvar = cvar->next)
2586                 cvar->globaldefindex[prog - prvm_prog_list] = -1;
2587
2588         for (i=0 ; i<prog->numglobaldefs ; i++)
2589         {
2590                 const char *name;
2591                 name = PRVM_GetString(prog, prog->globaldefs[i].s_name);
2592                 //Con_Printf("found var %s\n", name);
2593                 if(name
2594                         && !strncmp(name, "autocvar_", 9)
2595                         && !(strlen(name) > 1 && name[strlen(name)-2] == '_' && (name[strlen(name)-1] == 'x' || name[strlen(name)-1] == 'y' || name[strlen(name)-1] == 'z'))
2596                 )
2597                 {
2598                         prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
2599                         cvar = Cvar_FindVar(prog->console_cmd->cvars, name + 9, prog->console_cmd->cvars_flagsmask);
2600                         //Con_Printf("%s: autocvar global %s in %s, processing...\n", __func__, name, prog->name);
2601                         if(!cvar)
2602                         {
2603                                 const char *value;
2604                                 char buf[128];
2605                                 int prec[3];
2606                                 float f;
2607                                 Con_DPrintf("%s: no cvar for autocvar global %s in %s, creating...\n", __func__, name, prog->name);
2608                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2609                                 {
2610                                         case ev_float:
2611                                                 if((float)((int)(val->_float)) == val->_float)
2612                                                         dpsnprintf(buf, sizeof(buf), "%i", (int)(val->_float));
2613                                                 else
2614                                                 {
2615                                                         // ftos_slow
2616                                                         f = val->_float;
2617                                                         for (int precision = 7; precision <= 9; ++precision) {
2618                                                                 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2619                                                                 if ((float)atof(buf) == f) {
2620                                                                         break;
2621                                                                 }
2622                                                         }
2623                                                 }
2624                                                 value = buf;
2625                                                 break;
2626                                         case ev_vector:
2627                                                 for (i = 0; i < 3; ++i)
2628                                                 {
2629                                                         prec[i] = 9;
2630                                                         f = val->vector[i];
2631                                                         for (int precision = 7; precision <= 9; ++precision) {
2632                                                                 dpsnprintf(buf, sizeof(buf), "%.*g", precision, f);
2633                                                                 if ((float)atof(buf) == f) {
2634                                                                         prec[i] = precision;
2635                                                                         break;
2636                                                                 }
2637                                                         }
2638                                                 }
2639                                                 dpsnprintf(buf, sizeof(buf), "%.*g %.*g %.*g", prec[0], val->vector[0], prec[1], val->vector[1], prec[2], val->vector[2]);
2640                                                 value = buf;
2641                                                 break;
2642                                         case ev_string:
2643                                                 value = PRVM_GetString(prog, val->string);
2644                                                 break;
2645                                         default:
2646                                                 Con_Printf("%s: invalid type of autocvar global %s in %s\n", __func__, name, prog->name);
2647                                                 goto fail;
2648                                 }
2649                                 cvar = Cvar_Get(prog->console_cmd->cvars, name + 9, value, prog->console_cmd->cvars_flagsmask, NULL);
2650                                 if((prog->globaldefs[i].type & ~DEF_SAVEGLOBAL) == ev_string)
2651                                 {
2652                                         val->string = PRVM_SetEngineString(prog, cvar->string);
2653                                         cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2654                                 }
2655                                 if(!cvar)
2656                                         prog->error_cmd("%s: could not create cvar for autocvar global %s in %s", __func__, name, prog->name);
2657                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2658                         }
2659                         else if((cvar->flags & CF_PRIVATE) == 0)
2660                         {
2661                                 // MUST BE SYNCED WITH cvar.c Cvar_Set
2662                                 int j;
2663                                 const char *s;
2664                                 switch(prog->globaldefs[i].type & ~DEF_SAVEGLOBAL)
2665                                 {
2666                                         case ev_float:
2667                                                 val->_float = cvar->value;
2668                                                 break;
2669                                         case ev_vector:
2670                                                 s = cvar->string;
2671                                                 VectorClear(val->vector);
2672                                                 for (j = 0;j < 3;j++)
2673                                                 {
2674                                                         while (*s && ISWHITESPACE(*s))
2675                                                                 s++;
2676                                                         if (!*s)
2677                                                                 break;
2678                                                         val->vector[j] = atof(s);
2679                                                         while (!ISWHITESPACE(*s))
2680                                                                 s++;
2681                                                         if (!*s)
2682                                                                 break;
2683                                                 }
2684                                                 break;
2685                                         case ev_string:
2686                                                 val->string = PRVM_SetEngineString(prog, cvar->string);
2687                                                 cvar->globaldefindex_stringno[prog - prvm_prog_list] = val->string;
2688                                                 break;
2689                                         default:
2690                                                 Con_Printf("%s: invalid type of autocvar global %s in %s\n", __func__, name, prog->name);
2691                                                 goto fail;
2692                                 }
2693                                 cvar->globaldefindex[prog - prvm_prog_list] = i;
2694                         }
2695                         else
2696                                 Con_Printf("%s: private cvar for autocvar global %s in %s\n", __func__, name, prog->name);
2697                 }
2698 fail:
2699                 ;
2700         }
2701
2702         prog->loaded = true;
2703
2704         PRVM_UpdateBreakpoints(prog);
2705
2706         // set flags & mdef_ts in prog
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, new tempstring size %lu\n", prog->name, prog->tempstringsbuf.cursize, (unsigned long)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, new tempstring size %lu)\n", prog->name, prog->tempstringsbuf.cursize, (unsigned long)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 * (prog == SVVM_prog ? sv.frametime : cl.realframetime);
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 //              Con_Printf("%s%s GC: reset @ %f frametime %f scan_limit per frame %i\n", prog == SVVM_prog ? "^6" : "^5", prog->name, host.realtime, prog == SVVM_prog ? sv.frametime : cl.realframetime, limit);
3827         }
3828 }