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