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