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